001package gudusoft.gsqlparser.dlineage.symbol; 002 003import gudusoft.gsqlparser.TCustomSqlStatement; 004import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager; 005import gudusoft.gsqlparser.dlineage.dataflow.model.ModelFactory; 006import gudusoft.gsqlparser.dlineage.dataflow.model.OraclePackage; 007import gudusoft.gsqlparser.dlineage.dataflow.model.Procedure; 008import gudusoft.gsqlparser.dlineage.routine.RoutineAliasKey; 009import gudusoft.gsqlparser.stmt.TCommonBlock; 010import gudusoft.gsqlparser.stmt.TStoredProcedureSqlStatement; 011import gudusoft.gsqlparser.stmt.oracle.TPlsqlCreatePackage; 012import gudusoft.gsqlparser.stmt.teradata.TTeradataCreateProcedure; 013 014/** 015 * The scope a local symbol is declared in: the declaring routine's written name as frozen when the routine was 016 * declared (its model's display key, defaults included), prefixed by its package's; null at top level. It is found 017 * by the walk {@code DlineageUtil.getProcedureParentName} makes (the statement itself when it is a named routine, else 018 * the enclosing one, a block's owner included), but read from the routine's model, never re-composed from text with 019 * the session's defaults of the moment. 020 */ 021public final class LocalScopes { 022 023 private LocalScopes() { 024 } 025 026 /** The routine statement a statement's local symbols belong to, or null at top level. */ 027 public static TCustomSqlStatement routineOf(TCustomSqlStatement stmt) { 028 TCustomSqlStatement current = stmt; 029 boolean first = true; 030 while (current != null) { 031 if (current instanceof TStoredProcedureSqlStatement 032 && ((TStoredProcedureSqlStatement) current).getStoredProcedureName() != null) { 033 return current; 034 } 035 if (!first && current instanceof TTeradataCreateProcedure 036 && ((TTeradataCreateProcedure) current).getProcedureName() != null) { 037 return current; 038 } 039 first = false; 040 TCustomSqlStatement parent = current.getParentStmt(); 041 if (parent instanceof TCommonBlock && ((TCommonBlock) parent).getBlockBody() != null 042 && ((TCommonBlock) parent).getBlockBody().getParentObjectName() instanceof TStoredProcedureSqlStatement) { 043 parent = (TStoredProcedureSqlStatement) ((TCommonBlock) parent).getBlockBody().getParentObjectName(); 044 } 045 current = parent; 046 } 047 return null; 048 } 049 050 /** 051 * The frozen scope of a routine model: its display key, qualified by its package's; a routine declared inside 052 * another routine is scoped inside that routine's scope, so same-named nested routines of two routines are two 053 * scopes. 054 */ 055 public static Object scopeOf(ModelBindingManager manager, Procedure procedure) { 056 RoutineAliasKey own = ownScopeOf(procedure); 057 if (own == null || manager == null 058 || !(procedure.getProcedureObject() instanceof TCustomSqlStatement)) { 059 return own; 060 } 061 TCustomSqlStatement parent = ((TCustomSqlStatement) procedure.getProcedureObject()).getParentStmt(); 062 TCustomSqlStatement enclosing = parent == null ? null : routineOf(parent); 063 Procedure outer = enclosing == null ? null : manager.getRoutineModel(enclosing); 064 if (outer == null || outer == procedure) { 065 return own; 066 } 067 Object outerScope = scopeOf(manager, outer); 068 return outerScope == null ? own : new NestedScope(outerScope, own); 069 } 070 071 private static RoutineAliasKey ownScopeOf(Procedure procedure) { 072 if (procedure == null || procedure.getAliasKey() == null) { 073 return null; 074 } 075 if (procedure.getParentPackage() != null && procedure.getParentPackage().getAliasKey() != null) { 076 return procedure.getAliasKey().qualifiedBy(procedure.getParentPackage().getAliasKey()); 077 } 078 return procedure.getAliasKey(); 079 } 080 081 /** The scope of a routine declared inside another routine: the enclosing routine's scope, then its own. */ 082 static final class NestedScope { 083 private final Object outer; 084 private final Object own; 085 086 NestedScope(Object outer, Object own) { 087 this.outer = outer; 088 this.own = own; 089 } 090 091 @Override 092 public boolean equals(Object o) { 093 if (this == o) { 094 return true; 095 } 096 if (!(o instanceof NestedScope)) { 097 return false; 098 } 099 NestedScope that = (NestedScope) o; 100 return outer.equals(that.outer) && own.equals(that.own); 101 } 102 103 @Override 104 public int hashCode() { 105 return outer.hashCode() * 31 + own.hashCode(); 106 } 107 108 @Override 109 public String toString() { 110 return outer + "/" + own; 111 } 112 } 113 114 /** 115 * The scopes a statement's references see, innermost first: its routine's, the enclosing routines' (nested 116 * routines), then the package's. Never the top level: a routine body does not see a batch's or an anonymous 117 * block's variables. Empty at top level. 118 */ 119 public static java.util.List<Object> chainOf(ModelBindingManager manager, TCustomSqlStatement stmt) { 120 java.util.List<Object> chain = new java.util.ArrayList<Object>(); 121 TCustomSqlStatement routine = stmt == null ? null : routineOf(stmt); 122 Procedure innermost = null; 123 boolean first = true; 124 while (routine != null) { 125 Object scope = scopeOf(manager, routine); 126 if (scope != null && !chain.contains(scope)) { 127 chain.add(scope); 128 } 129 if (first) { 130 innermost = manager.getRoutineModel(routine); 131 first = false; 132 } 133 TCustomSqlStatement parent = routine.getParentStmt(); 134 routine = parent == null ? null : routineOf(parent); 135 } 136 if (innermost != null && innermost.getParentPackage() != null 137 && innermost.getParentPackage().getAliasKey() != null 138 && !chain.contains(innermost.getParentPackage().getAliasKey())) { 139 chain.add(innermost.getParentPackage().getAliasKey()); 140 } 141 return chain; 142 } 143 144 /** 145 * The scope of a statement's local symbols: its routine's frozen scope; a package's own key for declarations at 146 * package level (a package is a named routine statement with no routine model); null at top level. 147 */ 148 public static Object scopeOf(ModelBindingManager manager, TCustomSqlStatement stmt) { 149 TCustomSqlStatement routine = stmt == null ? null : routineOf(stmt); 150 if (routine == null) { 151 return null; 152 } 153 Procedure procedure = manager.getRoutineModel(routine); 154 if (procedure != null) { 155 return scopeOf(manager, procedure); 156 } 157 Object model = manager.getModel(routine); 158 if (!(model instanceof OraclePackage) && routine instanceof TPlsqlCreatePackage) { 159 // A package body is not bound to the package model its specification created; the package index is. 160 RoutineAliasKey[] keys = ModelFactory.packageAliasKeys((TStoredProcedureSqlStatement) routine); 161 model = keys == null ? null : manager.getOraclePackage(keys[0]); 162 } 163 return model instanceof OraclePackage ? ((OraclePackage) model).getAliasKey() : null; 164 } 165}