001package gudusoft.gsqlparser.resolver2.scope;
002
003import gudusoft.gsqlparser.nodes.TTable;
004import gudusoft.gsqlparser.resolver2.matcher.INameMatcher;
005import gudusoft.gsqlparser.resolver2.model.ScopeChild;
006import gudusoft.gsqlparser.resolver2.namespace.INamespace;
007
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.List;
011
012/**
013 * The relations of a FROM clause that come before one relation of it, {@code bound}: what the inputs of a
014 * table-generating relation such as a Hive-family LATERAL VIEW may read (nested-access plan section 4.3). It filters
015 * the live FROM scope when asked, by the statement's tables in source order, so it sees every relation to the left
016 * whatever order the scope builder added them in, and never one added to the right of {@code bound}.
017 */
018public final class LeftOfFromScope extends FromScope {
019
020    private final FromScope live;
021    private final List<TTable> sourceOrder;
022    private final TTable bound;
023
024    /**
025     * @param parent      the scope a name not found here is looked up in
026     * @param live        the FROM scope of the statement
027     * @param sourceOrder the statement's tables in source order ({@code TCustomSqlStatement.tables})
028     * @param bound       the relation whose left side this scope is
029     */
030    public LeftOfFromScope(IScope parent, FromScope live, List<TTable> sourceOrder, TTable bound) {
031        super(parent, live.getNode());
032        this.live = live;
033        this.sourceOrder = sourceOrder;
034        this.bound = bound;
035    }
036
037    private int position(TTable table) {
038        for (int i = 0; i < sourceOrder.size(); i++) {
039            if (sourceOrder.get(i) == table) {
040                return i;
041            }
042        }
043        return -1;
044    }
045
046    @Override
047    public List<ScopeChild> getChildren() {
048        int limit = position(bound);
049        List<ScopeChild> left = new ArrayList<ScopeChild>();
050        for (ScopeChild child : live.getChildren()) {
051            TTable table = child.getNamespace() == null ? null : child.getNamespace().getSourceTable();
052            int at = table == null ? -1 : position(table);
053            if (table != bound && (limit < 0 || at < 0 || at < limit)) {
054                left.add(child);
055            }
056        }
057        return Collections.unmodifiableList(left);
058    }
059
060    @Override
061    public void addChild(INamespace namespace, String alias, boolean nullable) {
062        throw new UnsupportedOperationException("a view of the FROM scope; add to the FROM scope itself");
063    }
064
065    @Override
066    public INamespace resolveTable(String tableName) {
067        INameMatcher matcher = new gudusoft.gsqlparser.resolver2.matcher.DefaultNameMatcher();
068        IScope cursor = this;
069        while (cursor != null && !(cursor instanceof EmptyScope)) {
070            if (cursor instanceof GlobalScope && ((GlobalScope) cursor).getNameMatcher() != null) {
071                matcher = ((GlobalScope) cursor).getNameMatcher();
072                break;
073            }
074            IScope next = cursor.getParent();
075            if (next == cursor) {
076                break;
077            }
078            cursor = next;
079        }
080        for (ScopeChild child : getChildren()) {
081            if (matcher.matches(child.getAlias(), tableName)) {
082                return child.getNamespace();
083            }
084        }
085        return null;
086    }
087
088    @Override
089    public List<INamespace> getVisibleNamespaces() {
090        List<INamespace> visible = new ArrayList<INamespace>();
091        for (ScopeChild child : getChildren()) {
092            visible.add(child.getNamespace());
093        }
094        return visible;
095    }
096}