001package gudusoft.gsqlparser.dlineage.dynamicsql; 002 003/** 004 * A concrete parameter binding value supplied by the caller of 005 * {@code DataFlowAnalyzer.resolveDynamicSqlLineage}. 006 * 007 * <p>This is the <em>input</em> side of dynamic-SQL resolution: the caller knows 008 * the value a parameter actually had at a call site (e.g. {@code @SourceDB='QSP'}) 009 * and passes it in. It is intentionally simple — a string, a number, or SQL 010 * {@code NULL}. The evaluator widens these into the internal 011 * {@link SqlStringValue} lattice (which also models <em>partial</em> and 012 * <em>unknown</em> states that a caller can never supply). 013 * 014 * <p>NULL is represented explicitly (not as a Java {@code null} binding) so the 015 * evaluator can honor T-SQL NULL-propagation semantics 016 * ({@code VARCHAR + NULL -> NULL}). A Java {@code null} key/value in the bindings 017 * map is treated as "no binding for this parameter". 018 */ 019public final class SqlValue { 020 021 private enum Kind { STRING, NUMBER, NULL } 022 023 private final Kind kind; 024 private final String text; 025 private final Double number; 026 027 private SqlValue(Kind kind, String text, Double number) { 028 this.kind = kind; 029 this.text = text; 030 this.number = number; 031 } 032 033 /** A string binding, e.g. {@code @SourceDB = 'QSP'}. */ 034 public static SqlValue of(String value) { 035 if (value == null) { 036 return nullValue(); 037 } 038 return new SqlValue(Kind.STRING, value, null); 039 } 040 041 /** An integer binding, e.g. {@code @Debug = 0}. Used for decidable IF and concatenation. */ 042 public static SqlValue of(long value) { 043 return new SqlValue(Kind.NUMBER, Long.toString(value), (double) value); 044 } 045 046 /** A floating-point binding. */ 047 public static SqlValue of(double value) { 048 return new SqlValue(Kind.NUMBER, formatNumber(value), value); 049 } 050 051 /** A boolean binding, mapped to T-SQL BIT semantics (1/0). */ 052 public static SqlValue of(boolean value) { 053 return of(value ? 1L : 0L); 054 } 055 056 /** An explicit SQL NULL binding. */ 057 public static SqlValue nullValue() { 058 return new SqlValue(Kind.NULL, null, null); 059 } 060 061 public boolean isNull() { 062 return kind == Kind.NULL; 063 } 064 065 public boolean isNumber() { 066 return kind == Kind.NUMBER; 067 } 068 069 /** The string form of the value (null only when {@link #isNull()}). */ 070 public String asText() { 071 return text; 072 } 073 074 /** The numeric value, or null when this is not a number binding. */ 075 public Double asNumber() { 076 return number; 077 } 078 079 static String formatNumber(double value) { 080 if (value == Math.rint(value) && !Double.isInfinite(value)) { 081 return Long.toString((long) value); 082 } 083 return Double.toString(value); 084 } 085 086 @Override 087 public String toString() { 088 switch (kind) { 089 case NULL: 090 return "NULL"; 091 case NUMBER: 092 return text; 093 default: 094 return "'" + text + "'"; 095 } 096 } 097}