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}