/**
   * INTERNAL: Return the value of the field from the row or a value holder on the query to obtain
   * the object. Check for batch + aggregation reading.
   */
  @Override
  public Object valueFromRow(
      AbstractRecord row,
      JoinedAttributeManager joinManager,
      ObjectBuildingQuery sourceQuery,
      AbstractSession executionSession)
      throws DatabaseException {
    // If any field in the foreign key is null then it means there are no referenced objects
    for (Enumeration enumeration = getFields().elements(); enumeration.hasMoreElements(); ) {
      DatabaseField field = (DatabaseField) enumeration.nextElement();
      if (row.get(field) == null) {
        return getIndirectionPolicy().nullValueFromRow();
      }
    }

    if (getTypeField() != null) {
      // If the query used batched reading, return a special value holder,
      // or retrieve the object from the query property.
      if (sourceQuery.isReadAllQuery()
          && (((ReadAllQuery) sourceQuery).isAttributeBatchRead(getDescriptor(), getAttributeName())
              || shouldUseBatchReading())) {
        return batchedValueFromRow(row, ((ReadAllQuery) sourceQuery));
      }

      // If the field is empty we cannot load the object because we do not know what class it will
      // be
      if (row.get(getTypeField()) == null) {
        return getIndirectionPolicy().nullValueFromRow();
      }
      Class implementerClass =
          (Class) getImplementorForType(row.get(getTypeField()), executionSession);
      ReadObjectQuery query = (ReadObjectQuery) getSelectionQuery().clone();
      query.setReferenceClass(implementerClass);
      query.setSelectionCriteria(getSelectionCriteria());
      query.setDescriptor(null); // Must set to null so the right descriptor is used

      if (sourceQuery.isObjectLevelReadQuery()
          && (sourceQuery.shouldCascadeAllParts()
              || (sourceQuery.shouldCascadePrivateParts() && isPrivateOwned())
              || (sourceQuery.shouldCascadeByMapping() && this.cascadeRefresh))) {
        query.setShouldRefreshIdentityMapResult(sourceQuery.shouldRefreshIdentityMapResult());
        query.setCascadePolicy(sourceQuery.getCascadePolicy());
        query.setShouldMaintainCache(sourceQuery.shouldMaintainCache());
        // For flashback.
        if (((ObjectLevelReadQuery) sourceQuery).hasAsOfClause()) {
          query.setAsOfClause(((ObjectLevelReadQuery) sourceQuery).getAsOfClause());
        }

        // CR #4365 - used to prevent infinit recursion on refresh object cascade all
        query.setQueryId(sourceQuery.getQueryId());
      }

      return getIndirectionPolicy().valueFromQuery(query, row, executionSession);
    } else {
      return super.valueFromRow(row, joinManager, sourceQuery, executionSession);
    }
  }
 /** Read employee and clear the cache, test database read. */
 public void test() throws Exception {
   ReadObjectQuery query = new ReadObjectQuery(Employee.class);
   ExpressionBuilder employee = new ExpressionBuilder();
   query.setSelectionCriteria(
       employee
           .get("firstName")
           .equal("Brendan")
           .and(employee.get("salary").equal(100000))
           .and(employee.get("address").get("city").like("%pean%"))
           .and(employee.anyOf("phoneNumbers").get("type").equal("Home")));
   query.checkCacheOnly();
   Employee result = (Employee) getSession().executeQuery(query);
 }
 /** INTERNAL: Select one object of any concrete subclass. */
 protected Object selectOneObject(ReadObjectQuery query) throws DescriptorException {
   ReadObjectQuery concreteQuery = (ReadObjectQuery) query.clone();
   Class javaClass = descriptor.getJavaClass();
   concreteQuery.setReferenceClass(javaClass);
   concreteQuery.setDescriptor(descriptor);
   return query.getSession().executeQuery(concreteQuery, concreteQuery.getTranslationRow());
 }
  /**
   * INTERNAL: Build and return the nested rows from the specified field value. This method allows
   * the field value to be an ARRAY containing other structures such as arrays or Struct, or direct
   * values.
   */
  public static Object buildContainerFromArray(
      Array fieldValue, ObjectRelationalDatabaseField arrayField, AbstractSession session)
      throws DatabaseException {
    if (arrayField.getType() == null) {
      return fieldValue;
    }
    Object[] objects = null;
    try {
      objects = (Object[]) fieldValue.getArray();
    } catch (java.sql.SQLException ex) {
      throw DatabaseException.sqlException(ex, session, false);
    }
    if (objects == null) {
      return null;
    }

    boolean isNestedStructure = false;
    ObjectRelationalDataTypeDescriptor ord = null;
    DatabaseField nestedType = null;
    if (arrayField != null) {
      nestedType = arrayField.getNestedTypeField();
      if ((nestedType != null) && nestedType.getSqlType() == Types.STRUCT) {
        ClassDescriptor descriptor = session.getDescriptor(nestedType.getType());
        if ((descriptor != null) && (descriptor.isObjectRelationalDataTypeDescriptor())) {
          // this is used to convert non-null objects passed through stored procedures and custom
          // SQL to structs
          ord = (ObjectRelationalDataTypeDescriptor) descriptor;
        }
      } else if ((nestedType != null) && (nestedType instanceof ObjectRelationalDatabaseField)) {
        isNestedStructure = true;
      }
    }
    // handle ARRAY conversions
    ReadObjectQuery query = new ReadObjectQuery();
    query.setSession(session);
    ContainerPolicy cp = ContainerPolicy.buildPolicyFor(arrayField.getType());
    Object container = cp.containerInstance(objects.length);
    for (int i = 0; i < objects.length; i++) {
      Object arrayValue = objects[i];
      if (arrayValue == null) {
        return null;
      }
      if (ord != null) {
        AbstractRecord nestedRow = ord.buildRowFromStructure((Struct) arrayValue);
        ClassDescriptor descriptor = ord;
        if (descriptor.hasInheritance()) {
          Class newElementClass =
              descriptor.getInheritancePolicy().classFromRow(nestedRow, session);
          if (!descriptor.getJavaClass().equals(newElementClass)) {
            descriptor = session.getDescriptor(newElementClass);
            if (descriptor == null) {
              descriptor = ord;
            }
          }
        }
        arrayValue = descriptor.getObjectBuilder().buildNewInstance();
        descriptor
            .getObjectBuilder()
            .buildAttributesIntoObject(arrayValue, nestedRow, query, null, false);
      } else if (isNestedStructure && (arrayValue instanceof Array)) {
        arrayValue =
            buildContainerFromArray(
                (Array) arrayValue, (ObjectRelationalDatabaseField) nestedType, session);
      }

      cp.addInto(arrayValue, container, session);
    }
    return container;
  }