docs: protected void validateSelect(
  		SqlSelect select,
  		RelDataType targetRowType) {
  		assert targetRowType != null;
  		// Namespace is either a select namespace or a wrapper around one.
  		final SelectNamespace ns =
  			getNamespace(select).unwrap(SelectNamespace.class);
  
  		// Its rowtype is null, meaning it hasn't been validated yet.
  		// This is important, because we need to take the targetRowType into
  		// account.
  		assert ns.rowType == null;
  
  		if (select.isDistinct()) {
  			validateFeature(RESOURCE.sQLFeature_E051_01(),
  				select.getModifierNode(SqlSelectKeyword.DISTINCT)
  					.getParserPosition());
  		}
  
  		final SqlNodeList selectItems = select.getSelectList();
  		RelDataType fromType = unknownType;
  		if (selectItems.size() == 1) {
  			final SqlNode selectItem = selectItems.get(0);
  			if (selectItem instanceof SqlIdentifier) {
  				SqlIdentifier id = (SqlIdentifier) selectItem;
  				if (id.isStar() && (id.names.size() == 1)) {
  					// Special case: for INSERT ... VALUES(?,?), the SQL
  					// standard says we're supposed to propagate the target
  					// types down.  So iff the select list is an unqualified
  					// star (as it will be after an INSERT ... VALUES has been
  					// expanded), then propagate.
  					fromType = targetRowType;
  				}
  			}
  		}
  
  		// Make sure that items in FROM clause have distinct aliases.
  		final SelectScope fromScope = (SelectScope) getFromScope(select);
  		List<String> names = fromScope.getChildNames();
  		if (!catalogReader.nameMatcher().isCaseSensitive()) {
  			names = Lists.transform(names, s -> s.toUpperCase(Locale.ROOT));
  		}
  		final int duplicateAliasOrdinal = Util.firstDuplicate(names);
  		if (duplicateAliasOrdinal >= 0) {
  			final ScopeChild child =
  				fromScope.children.get(duplicateAliasOrdinal);
  			throw newValidationError(child.namespace.getEnclosingNode(),
  				RESOURCE.fromAliasDuplicate(child.name));
  		}
  
  		if (select.getFrom() == null) {
  			if (conformance.isFromRequired()) {
  				throw newValidationError(select, RESOURCE.selectMissingFrom());
  			}
  		} else {
  			validateFrom(select.getFrom(), fromType, fromScope);
  		}
  
  		validateWhereClause(select);
  		validateGroupClause(select);
  		validateHavingClause(select);
  		validateWindowClause(select);
  		handleOffsetFetch(select.getOffset(), select.getFetch());
  
  		// Validate the SELECT clause late, because a select item might
  		// depend on the GROUP BY list, or the window function might reference
  		// window name in the WINDOW clause etc.
  		final RelDataType rowType =
  			validateSelectList(selectItems, select, targetRowType);
  		ns.setType(rowType);
  
  		// Validate ORDER BY after we have set ns.rowType because in some
  		// dialects you can refer to columns of the select list, e.g.
  		// "SELECT empno AS x FROM emp ORDER BY x"
  		validateOrderList(select);
  
  		if (shouldCheckForRollUp(select.getFrom())) {
  			checkRollUpInSelectList(select);
  			checkRollUp(null, select, select.getWhere(), getWhereScope(select));
  			checkRollUp(null, select, select.getHaving(), getHavingScope(select));
  			checkRollUpInWindowDecl(select);
  			checkRollUpInGroupBy(select);
  			checkRollUpInOrderBy(select);
  		}
  	}