001package gudusoft.gsqlparser.resolver2.namespace; 002 003import gudusoft.gsqlparser.TBaseType; 004import gudusoft.gsqlparser.nodes.TTable; 005import gudusoft.gsqlparser.resolver2.ColumnLevel; 006import gudusoft.gsqlparser.resolver2.model.ColumnSource; 007import gudusoft.gsqlparser.stmt.TVarDeclStmt; 008 009import java.util.*; 010 011/** 012 * Namespace representing PL/SQL variables declared in a block. 013 * 014 * <p>PL/SQL blocks (<<label>>) can declare variables in their DECLARE section. 015 * These variables can be referenced using the block label as a qualifier: 016 * <pre> 017 * <<main>> 018 * DECLARE 019 * ename VARCHAR2(10) := 'KING'; 020 * BEGIN 021 * DELETE FROM emp WHERE ename = main.ename; -- main.ename is the variable 022 * END; 023 * </pre> 024 * 025 * <p>This namespace allows the resolver to distinguish between PL/SQL variables 026 * (like main.ename) and table columns (like emp.ename) by registering the 027 * block's variables as a namespace that can be queried by the block's label. 028 */ 029public class PlsqlVariableNamespace implements INamespace { 030 031 /** The block label (e.g., "main" from <<main>>) */ 032 private final String blockLabel; 033 034 /** Map of variable name (lowercase) -> variable declaration */ 035 private final Map<String, TVarDeclStmt> variables = new LinkedHashMap<>(); 036 037 /** Map of column name (lowercase) -> ColumnSource for the variables */ 038 private final Map<String, ColumnSource> columnSources = new LinkedHashMap<>(); 039 040 /** Set of parameter names (lowercase) for procedure/function parameters */ 041 private final Set<String> parameterNames = new HashSet<>(); 042 043 /** Whether this namespace has been validated */ 044 private boolean validated = false; 045 046 /** 047 * Create a new PL/SQL variable namespace for a labeled block. 048 * 049 * @param blockLabel The block label (e.g., "main" from <<main>>) 050 */ 051 public PlsqlVariableNamespace(String blockLabel) { 052 this.blockLabel = blockLabel; 053 } 054 055 /** 056 * Add a variable declaration to this namespace. 057 * 058 * @param varDecl The variable declaration statement 059 */ 060 public void addVariable(TVarDeclStmt varDecl) { 061 if (varDecl != null && varDecl.getElementName() != null) { 062 String varName = varDecl.getElementName().toString(); 063 // Strip quotes from variable names (e.g., "myvar" -> myvar) 064 String varNameNormalized = TBaseType.getTextWithoutQuoted(varName); 065 String varNameLower = varNameNormalized.toLowerCase(); 066 variables.put(varNameLower, varDecl); 067 // Create a ColumnSource with the variable declaration as the definition node 068 // This marks the source as coming from a PL/SQL block, not a table 069 ColumnSource source = new ColumnSource(this, varNameNormalized, varDecl.getElementName(), 1.0, "plsql_variable"); 070 columnSources.put(varNameLower, source); 071 } 072 } 073 074 /** 075 * Add a procedure/function parameter to this namespace. 076 * Parameters are tracked separately from variables since they don't need 077 * ColumnSource lineage - we only need to know if a name is a parameter 078 * to filter it from column reference collection. 079 * 080 * @param paramName The parameter name (as String) 081 */ 082 public void addParameter(String paramName) { 083 if (paramName != null && !paramName.isEmpty()) { 084 // Strip quotes from parameter names (e.g., "A1" -> A1) 085 String normalized = TBaseType.getTextWithoutQuoted(paramName).toLowerCase(); 086 parameterNames.add(normalized); 087 } 088 } 089 090 /** 091 * Check if a variable with the given name exists in this block. 092 * 093 * @param variableName The variable name to check 094 * @return true if the variable exists 095 */ 096 public boolean hasVariable(String variableName) { 097 String normalized = TBaseType.getTextWithoutQuoted(variableName).toLowerCase(); 098 return variables.containsKey(normalized); 099 } 100 101 @Override 102 public String getDisplayName() { 103 return blockLabel != null ? blockLabel : "(anonymous block)"; 104 } 105 106 /** 107 * Check if a name is a parameter. 108 * 109 * @param name The name to check 110 * @return true if the name is a registered parameter 111 */ 112 public boolean hasParameter(String name) { 113 String normalized = TBaseType.getTextWithoutQuoted(name).toLowerCase(); 114 return parameterNames.contains(normalized); 115 } 116 117 @Override 118 public ColumnLevel hasColumn(String columnName) { 119 // Strip quotes and normalize to lowercase for comparison 120 String nameLower = TBaseType.getTextWithoutQuoted(columnName).toLowerCase(); 121 // Check variables, parameters, and columnSources 122 if (variables.containsKey(nameLower) || parameterNames.contains(nameLower)) { 123 return ColumnLevel.EXISTS; 124 } 125 return ColumnLevel.NOT_EXISTS; 126 } 127 128 @Override 129 public ColumnSource resolveColumn(String columnName) { 130 String nameLower = TBaseType.getTextWithoutQuoted(columnName).toLowerCase(); 131 return columnSources.get(nameLower); 132 } 133 134 @Override 135 public Map<String, ColumnSource> getAllColumnSources() { 136 return Collections.unmodifiableMap(columnSources); 137 } 138 139 @Override 140 public TTable getFinalTable() { 141 // PL/SQL variables don't have a physical table 142 return null; 143 } 144 145 @Override 146 public List<TTable> getAllFinalTables() { 147 // PL/SQL variables don't have physical tables 148 return Collections.emptyList(); 149 } 150 151 @Override 152 public boolean isValidated() { 153 return validated; 154 } 155 156 @Override 157 public void validate() { 158 validated = true; 159 } 160 161 @Override 162 public Object getNode() { 163 return null; 164 } 165 166 /** 167 * Get the block label. 168 * 169 * @return The block label 170 */ 171 public String getBlockLabel() { 172 return blockLabel; 173 } 174 175 /** 176 * Get all variable declarations in this block. 177 * 178 * @return Unmodifiable map of variable name -> declaration 179 */ 180 public Map<String, TVarDeclStmt> getVariables() { 181 return Collections.unmodifiableMap(variables); 182 } 183 184 @Override 185 public String toString() { 186 return "PlsqlVariableNamespace(" + getDisplayName() + 187 ", vars=" + variables.size() + 188 ", params=" + parameterNames.size() + ")"; 189 } 190}