private void validate(
      VDBMetaData vdb,
      ModelMetaData model,
      AbstractMetadataRecord record,
      ValidatorReport report,
      IQueryMetadataInterface metadata,
      MetadataFactory mf) {
    ValidatorReport resolverReport = null;
    try {
      if (record instanceof Procedure) {
        Procedure p = (Procedure) record;
        Command command = queryParser.parseProcedure(p.getQueryPlan(), false);
        GroupSymbol gs = createASTNode(ASTNodes.GROUP_SYMBOL);
        gs.setName(p.getFullName());
        QueryResolver resolver = new QueryResolver(queryParser);
        resolver.resolveCommand(command, gs, ICommand.TYPE_STORED_PROCEDURE, metadata, false);
        Validator validator = new Validator();
        resolverReport = validator.validate(command, metadata);
        determineDependencies(p, command);
      } else if (record instanceof Table) {
        Table t = (Table) record;

        GroupSymbol symbol = createASTNode(ASTNodes.GROUP_SYMBOL);
        symbol.setName(t.getFullName());
        ResolverUtil.resolveGroup(symbol, metadata);
        String selectTransformation = t.getSelectTransformation();

        boolean columnsIsEmpty = t.getColumns() == null || t.getColumns().isEmpty();

        // Consider columns if teid 8.11 or lower
        boolean considerColumns_811 =
            isTeiidOrGreater(Version.TEIID_8_12_4) ? true : columnsIsEmpty;
        // Consider columns if teiid 8.12.4+
        boolean considerColumns_8124 =
            isTeiidOrGreater(Version.TEIID_8_12_4) ? columnsIsEmpty : true;

        if (t.isVirtual() && considerColumns_811) {
          QueryCommand command = (QueryCommand) queryParser.parseCommand(selectTransformation);
          QueryResolver resolver = new QueryResolver(queryParser);
          resolver.resolveCommand(command, metadata);
          Validator validator = new Validator();
          resolverReport = validator.validate(command, metadata);

          if (!resolverReport.hasItems() && considerColumns_8124) {
            List<Expression> symbols = command.getProjectedSymbols();
            for (Expression column : symbols) {
              try {
                addColumn(Symbol.getShortName(column), column.getType(), t, mf);
              } catch (Exception e) {
                log(report, model, e.getMessage());
              }
            }
          }

          if (considerColumns_8124) {
            determineDependencies(t, command);
            if (t.getInsertPlan() != null && t.isInsertPlanEnabled()) {
              validateUpdatePlan(
                  model, report, metadata, t, t.getInsertPlan(), Command.TYPE_INSERT);
            }
            if (t.getUpdatePlan() != null && t.isUpdatePlanEnabled()) {
              validateUpdatePlan(
                  model, report, metadata, t, t.getUpdatePlan(), Command.TYPE_UPDATE);
            }
            if (t.getDeletePlan() != null && t.isDeletePlanEnabled()) {
              validateUpdatePlan(
                  model, report, metadata, t, t.getDeletePlan(), Command.TYPE_DELETE);
            }
          }
        }

        boolean addCacheHint = false;
        if (t.isMaterialized() && t.getMaterializedTable() == null) {
          List<KeyRecord> fbis = t.getFunctionBasedIndexes();
          List<GroupSymbol> groups = Arrays.asList(symbol);
          if (fbis != null && !fbis.isEmpty()) {
            for (KeyRecord fbi : fbis) {
              for (int j = 0; j < fbi.getColumns().size(); j++) {
                Column c = fbi.getColumns().get(j);
                if (c.getParent() != fbi) {
                  continue;
                }
                String exprString = c.getNameInSource();
                try {
                  Expression ex = queryParser.parseExpression(exprString);
                  ResolverVisitor resolverVisitor = new ResolverVisitor(teiidVersion);
                  resolverVisitor.resolveLanguageObject(ex, groups, metadata);
                  if (!ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(ex)
                      .isEmpty()) {
                    log(
                        report,
                        model,
                        Messages.gs(Messages.TEIID.TEIID31114, exprString, fbi.getFullName()));
                  }
                  EvaluatableVisitor ev = new EvaluatableVisitor(teiidVersion);
                  PreOrPostOrderNavigator.doVisit(ex, ev, PostOrderNavigator.PRE_ORDER);
                  if (ev.getDeterminismLevel().compareTo(Determinism.VDB_DETERMINISTIC) < 0) {
                    log(
                        report,
                        model,
                        Messages.gs(Messages.TEIID.TEIID31115, exprString, fbi.getFullName()));
                  }
                } catch (QueryResolverException e) {
                  log(
                      report,
                      model,
                      Messages.gs(
                          Messages.TEIID.TEIID31116,
                          exprString,
                          fbi.getFullName(),
                          e.getMessage()));
                }
              }
            }
          }
        } else {
          addCacheHint = true;
        }

        // this seems to parse, resolve and validate.
        QueryResolver resolver = new QueryResolver(queryParser);
        QueryNode node =
            resolver.resolveView(
                symbol,
                new QueryNode(t.getSelectTransformation()),
                SQLConstants.Reserved.SELECT,
                metadata);
        CacheHint cacheHint = node.getCommand().getCacheHint();
        Long ttl = -1L;
        if (cacheHint != null
            && cacheHint.getTtl() != null
            && addCacheHint
            && t.getProperty(MATVIEW_TTL, false) == null) {
          ttl = cacheHint.getTtl();
          t.setProperty(MATVIEW_TTL, String.valueOf(ttl));
        }
      }
      if (resolverReport != null && resolverReport.hasItems()) {
        for (ValidatorFailure v : resolverReport.getItems()) {
          log(
              report,
              model,
              v.getStatus() == IValidatorFailure.VFStatus.ERROR ? Severity.ERROR : Severity.WARNING,
              v.getMessage());
        }
      }
      processReport(model, record, report, resolverReport);
    } catch (Exception e) {
      log(
          report,
          model,
          Messages.gs(Messages.TEIID.TEIID31080, record.getFullName(), e.getMessage()));
    }
  }
 /**
  * Helper to quickly get the ValueIteratorProvider instances from obj
  *
  * @param obj Language object
  * @return java.util.ArrayList of found ValueIteratorProvider
  */
 public static final List<SubqueryContainer<?>> getValueIteratorProviders(LanguageObject obj) {
   ValueIteratorProviderCollectorVisitor visitor = new ValueIteratorProviderCollectorVisitor();
   PreOrderNavigator.doVisit(obj, visitor);
   return visitor.getValueIteratorProviders();
 }
  private void resolveStatement(
      CreateProcedureCommand command,
      Statement statement,
      GroupContext externalGroups,
      GroupSymbol variables,
      TempMetadataAdapter metadata)
      throws QueryResolverException, QueryMetadataException, TeiidComponentException {
    LogManager.logTrace(
        org.teiid.logging.LogConstants.CTX_QUERY_RESOLVER,
        new Object[] {"Resolving statement", statement}); // $NON-NLS-1$

    switch (statement.getType()) {
      case Statement.TYPE_IF:
        IfStatement ifStmt = (IfStatement) statement;
        Criteria ifCrit = ifStmt.getCondition();
        for (SubqueryContainer container :
            ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(ifCrit)) {
          resolveEmbeddedCommand(metadata, externalGroups, container.getCommand());
        }
        ResolverVisitor.resolveLanguageObject(ifCrit, null, externalGroups, metadata);
        resolveBlock(command, ifStmt.getIfBlock(), externalGroups, metadata);
        if (ifStmt.hasElseBlock()) {
          resolveBlock(command, ifStmt.getElseBlock(), externalGroups, metadata);
        }
        break;
      case Statement.TYPE_COMMAND:
        CommandStatement cmdStmt = (CommandStatement) statement;
        Command subCommand = cmdStmt.getCommand();

        TempMetadataStore discoveredMetadata =
            resolveEmbeddedCommand(metadata, externalGroups, subCommand);

        if (subCommand instanceof StoredProcedure) {
          StoredProcedure sp = (StoredProcedure) subCommand;
          for (SPParameter param : sp.getParameters()) {
            switch (param.getParameterType()) {
              case ParameterInfo.OUT:
              case ParameterInfo.RETURN_VALUE:
                if (param.getExpression() != null && !isAssignable(metadata, param)) {
                  throw new QueryResolverException(
                      QueryPlugin.Event.TEIID30121,
                      QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30121, param.getExpression()));
                }
                sp.setCallableStatement(true);
                break;
              case ParameterInfo.INOUT:
                if (!isAssignable(metadata, param)) {
                  continue;
                }
                sp.setCallableStatement(true);
                break;
            }
          }
        }

        if (discoveredMetadata != null) {
          metadata.getMetadataStore().getData().putAll(discoveredMetadata.getData());
        }

        // dynamic commands need to be updated as to their implicitly expected projected symbols
        if (subCommand instanceof DynamicCommand) {
          DynamicCommand dynCommand = (DynamicCommand) subCommand;

          if (dynCommand.getIntoGroup() == null && !dynCommand.isAsClauseSet()) {
            if ((command.getResultSetColumns() != null && command.getResultSetColumns().isEmpty())
                || !cmdStmt.isReturnable()
                || command.getResultSetColumns() == null) {
              // we're not interested in the resultset
              dynCommand.setAsColumns(Collections.EMPTY_LIST);
            } else {
              // should match the procedure
              dynCommand.setAsColumns(command.getResultSetColumns());
            }
          }
        }

        if (command.getResultSetColumns() == null
            && cmdStmt.isReturnable()
            && subCommand.returnsResultSet()
            && subCommand.getResultSetColumns() != null
            && !subCommand.getResultSetColumns().isEmpty()) {
          command.setResultSetColumns(subCommand.getResultSetColumns());
          if (command.getProjectedSymbols().isEmpty()) {
            command.setProjectedSymbols(subCommand.getResultSetColumns());
          }
        }

        break;
      case Statement.TYPE_ERROR:
      case Statement.TYPE_ASSIGNMENT:
      case Statement.TYPE_DECLARE:
      case Statement.TYPE_RETURN:
        ExpressionStatement exprStmt = (ExpressionStatement) statement;
        // first resolve the value.  this ensures the value cannot use the variable being defined
        if (exprStmt.getExpression() != null) {
          Expression expr = exprStmt.getExpression();
          for (SubqueryContainer container :
              ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(expr)) {
            resolveEmbeddedCommand(metadata, externalGroups, container.getCommand());
          }
          ResolverVisitor.resolveLanguageObject(expr, null, externalGroups, metadata);
        }

        // second resolve the variable
        switch (statement.getType()) {
          case Statement.TYPE_DECLARE:
            collectDeclareVariable(
                (DeclareStatement) statement, variables, metadata, externalGroups);
            break;
          case Statement.TYPE_ASSIGNMENT:
            AssignmentStatement assStmt = (AssignmentStatement) statement;
            ResolverVisitor.resolveLanguageObject(
                assStmt.getVariable(), null, externalGroups, metadata);
            if (!metadata.elementSupports(
                assStmt.getVariable().getMetadataID(), SupportConstants.Element.UPDATE)) {
              throw new QueryResolverException(
                  QueryPlugin.Event.TEIID30121,
                  QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30121, assStmt.getVariable()));
            }
            // don't allow variable assignments to be external
            assStmt.getVariable().setIsExternalReference(false);
            break;
          case Statement.TYPE_RETURN:
            ReturnStatement rs = (ReturnStatement) statement;
            if (rs.getExpression() != null) {
              if (command.getReturnVariable() == null) {
                throw new QueryResolverException(
                    QueryPlugin.Event.TEIID31125,
                    QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31125, rs));
              }
              rs.setVariable(command.getReturnVariable().clone());
            }
            // else - we don't currently require the use of return for backwards compatibility
            break;
        }

        // third ensure the type matches
        if (exprStmt.getExpression() != null) {
          Class<?> varType = exprStmt.getExpectedType();
          Class<?> exprType = exprStmt.getExpression().getType();
          if (exprType == null) {
            throw new QueryResolverException(
                QueryPlugin.Event.TEIID30123, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30123));
          }
          String varTypeName = DataTypeManager.getDataTypeName(varType);
          exprStmt.setExpression(
              ResolverUtil.convertExpression(exprStmt.getExpression(), varTypeName, metadata));
          if (statement.getType() == Statement.TYPE_ERROR) {
            ResolverVisitor.checkException(exprStmt.getExpression());
          }
        }
        break;
      case Statement.TYPE_WHILE:
        WhileStatement whileStmt = (WhileStatement) statement;
        Criteria whileCrit = whileStmt.getCondition();
        for (SubqueryContainer container :
            ValueIteratorProviderCollectorVisitor.getValueIteratorProviders(whileCrit)) {
          resolveEmbeddedCommand(metadata, externalGroups, container.getCommand());
        }
        ResolverVisitor.resolveLanguageObject(whileCrit, null, externalGroups, metadata);
        resolveBlock(command, whileStmt.getBlock(), externalGroups, metadata);
        break;
      case Statement.TYPE_LOOP:
        LoopStatement loopStmt = (LoopStatement) statement;
        String groupName = loopStmt.getCursorName();

        isValidGroup(metadata, groupName);
        Command cmd = loopStmt.getCommand();
        resolveEmbeddedCommand(metadata, externalGroups, cmd);
        List<Expression> symbols = cmd.getProjectedSymbols();

        // add the loop cursor group into its own context
        TempMetadataStore store = metadata.getMetadataStore().clone();
        metadata = new TempMetadataAdapter(metadata.getMetadata(), store);
        externalGroups = new GroupContext(externalGroups, null);

        ProcedureContainerResolver.addScalarGroup(groupName, store, externalGroups, symbols, false);

        resolveBlock(command, loopStmt.getBlock(), externalGroups, metadata);
        break;
      case Statement.TYPE_COMPOUND:
        resolveBlock(command, (Block) statement, externalGroups, metadata);
        break;
    }
  }