001package gudusoft.gsqlparser.dlineage.dynamicsql; 002 003import gudusoft.gsqlparser.TCustomSqlStatement; 004import gudusoft.gsqlparser.nodes.TObjectName; 005 006import java.util.Collections; 007import java.util.List; 008import java.util.Map; 009 010/** 011 * B0 CONTRACT (design {@code docs/designs/sp/routine-summary-scc-design.md} 012 * §2.3): the one canonical call-site binding interface. Implementations 013 * arrive in slice B3 by adapting the existing vendor extractors (P5 014 * literal/EXEC/USE/DBMS_SQL machinery becomes the lower layer); B0 ships the 015 * contract only, so that B2 summary extraction and B3 application code are 016 * written against a stable shape. 017 * 018 * <p>Binding rules the contract encodes (normative, from the design): 019 * <ul> 020 * <li>expression actuals bind as a FAN-IN of all leaf source columns plus a 021 * transform — never a single-column substitution;</li> 022 * <li>OUT/INOUT/RETURN targets must be validated writable lvalues; an invalid 023 * OUT actual is diagnosed and causes summary application to quarantine 024 * the whole non-executable call, never fabricate a partial effect;</li> 025 * <li>literal actuals produce no source (matching the inline engine);</li> 026 * <li>INSERT…EXEC result-set consumption maps positionally, not via 027 * formals;</li> 028 * <li>an unresolved or ambiguous callee binds NOTHING — ambiguity is 029 * reported, never guessed through.</li> 030 * </ul> 031 */ 032public interface RoutineCallBinder { 033 034 /** 035 * Resolve and bind one call site against the candidate definitions 036 * visible to the analysis. 037 * 038 * @param callSite the call statement (EXEC / CALL / function reference) 039 * @param callerContext effective caller context (USE database, schema) 040 * @return never null; {@link BindingResult#getStatus()} reports failures 041 */ 042 BindingResult bind(TCustomSqlStatement callSite, CallerContext callerContext); 043 044 /** Effective ambient context of the caller at the call site. */ 045 final class CallerContext { 046 private final String database; 047 private final String schema; 048 049 public CallerContext(String database, String schema) { 050 this.database = database; 051 this.schema = schema; 052 } 053 054 public String getDatabase() { return database; } 055 public String getSchema() { return schema; } 056 } 057 058 /** Outcome status of a bind attempt. */ 059 enum BindingStatus { 060 /** Unique callee resolved; maps populated per the contract. */ 061 RESOLVED, 062 /** Multiple compatible definitions — nothing bound, candidates reported. */ 063 AMBIGUOUS, 064 /** Callee known only by signature (metadata) — binding maps populated, no body. */ 065 EXTERNAL, 066 /** No candidate definition found. */ 067 UNRESOLVED 068 } 069 070 /** Completeness of the produced maps for a RESOLVED/EXTERNAL bind. */ 071 enum BindingCompleteness { 072 COMPLETE, 073 /** Some arguments could not be bound; reasons in diagnostics. */ 074 PARTIAL 075 } 076 077 /** Diagnostic codes surfaced by binding (design §2.1b/§2.3). */ 078 enum BindingDiagnosticCode { 079 PROC_CALL_AMBIGUOUS_OVERLOAD, 080 PROC_CALL_UNRESOLVED, 081 PROC_CALL_OUT_TARGET_NOT_WRITABLE, 082 /** Call-site OUTPUT was supplied for a formal not declared OUTPUT. */ 083 PROC_CALL_OUTPUT_MODE_MISMATCH, 084 PROC_CALL_ARGUMENT_UNBOUND, 085 PROC_CALL_EXTERNAL_SIGNATURE_ONLY 086 } 087 088 /** One structured diagnostic attached to a bind. */ 089 final class BindingDiagnostic { 090 private final BindingDiagnosticCode code; 091 private final String detail; 092 093 public BindingDiagnostic(BindingDiagnosticCode code, String detail) { 094 this.code = code; 095 this.detail = detail == null ? "" : detail; 096 } 097 098 public BindingDiagnosticCode getCode() { return code; } 099 public String getDetail() { return detail; } 100 } 101 102 /** 103 * The leaf sources feeding one IN/INOUT formal: zero or more source 104 * column references (zero for a pure literal actual) plus the actual 105 * expression text when the actual is not a bare column/variable 106 * (the transform carrier of the fan-in rule). 107 */ 108 final class ActualSources { 109 private final List<TObjectName> leafSources; 110 private final String expressionText; 111 112 public ActualSources(List<TObjectName> leafSources, String expressionText) { 113 this.leafSources = leafSources == null 114 ? Collections.<TObjectName>emptyList() 115 : Collections.unmodifiableList(leafSources); 116 this.expressionText = expressionText; 117 } 118 119 public List<TObjectName> getLeafSources() { return leafSources; } 120 /** Null when the actual is a bare column/variable or literal. */ 121 public String getExpressionText() { return expressionText; } 122 } 123 124 /** A validated writable lvalue receiving an OUT/INOUT/RETURN/status flow. */ 125 final class WritableTarget { 126 private final TObjectName target; 127 128 public WritableTarget(TObjectName target) { 129 this.target = target; 130 } 131 132 public TObjectName getTarget() { return target; } 133 } 134 135 /** 136 * Positional mapping of a consumed result set (INSERT…EXEC and friends): 137 * ordinal position in the routine's result-set shape → target column. 138 */ 139 final class ResultSetMapping { 140 private final List<TObjectName> positionalTargets; 141 142 public ResultSetMapping(List<TObjectName> positionalTargets) { 143 this.positionalTargets = positionalTargets == null 144 ? Collections.<TObjectName>emptyList() 145 : Collections.unmodifiableList(positionalTargets); 146 } 147 148 public List<TObjectName> getPositionalTargets() { return positionalTargets; } 149 } 150 151 /** 152 * Canonical result of binding one call site (design §2.3). Formals are 153 * addressed by declaration position (index into the resolved identity's 154 * parameter lists) so the result never depends on name spelling. 155 */ 156 final class BindingResult { 157 private final BindingStatus status; 158 private final RoutineIdentity resolved; 159 private final List<RoutineIdentity> candidates; 160 private final Map<Integer, ActualSources> inputs; 161 private final Map<Integer, WritableTarget> outputs; 162 private final WritableTarget returnTarget; 163 private final ResultSetMapping resultSets; 164 private final BindingCompleteness completeness; 165 private final List<BindingDiagnostic> diagnostics; 166 167 public BindingResult(BindingStatus status, RoutineIdentity resolved, 168 List<RoutineIdentity> candidates, 169 Map<Integer, ActualSources> inputs, 170 Map<Integer, WritableTarget> outputs, 171 WritableTarget returnTarget, ResultSetMapping resultSets, 172 BindingCompleteness completeness, 173 List<BindingDiagnostic> diagnostics) { 174 this.status = status; 175 this.resolved = resolved; 176 this.candidates = candidates == null 177 ? Collections.<RoutineIdentity>emptyList() 178 : Collections.unmodifiableList(candidates); 179 this.inputs = inputs == null 180 ? Collections.<Integer, ActualSources>emptyMap() 181 : Collections.unmodifiableMap(inputs); 182 this.outputs = outputs == null 183 ? Collections.<Integer, WritableTarget>emptyMap() 184 : Collections.unmodifiableMap(outputs); 185 this.returnTarget = returnTarget; 186 this.resultSets = resultSets; 187 this.completeness = completeness == null 188 ? BindingCompleteness.PARTIAL : completeness; 189 this.diagnostics = diagnostics == null 190 ? Collections.<BindingDiagnostic>emptyList() 191 : Collections.unmodifiableList(diagnostics); 192 } 193 194 public BindingStatus getStatus() { return status; } 195 /** Non-null only when status is RESOLVED or EXTERNAL. */ 196 public RoutineIdentity getResolved() { return resolved; } 197 /** Populated when status is AMBIGUOUS. */ 198 public List<RoutineIdentity> getCandidates() { return candidates; } 199 /** Formal position → sources feeding it (IN/INOUT reads). */ 200 public Map<Integer, ActualSources> getInputs() { return inputs; } 201 /** Formal position → validated caller target (OUT/INOUT writes). */ 202 public Map<Integer, WritableTarget> getOutputs() { return outputs; } 203 /** Null unless a function return / status flows to a caller target. */ 204 public WritableTarget getReturnTarget() { return returnTarget; } 205 /** Null unless the call site consumes a result set positionally. */ 206 public ResultSetMapping getResultSets() { return resultSets; } 207 public BindingCompleteness getCompleteness() { return completeness; } 208 public List<BindingDiagnostic> getDiagnostics() { return diagnostics; } 209 } 210}