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 /** Raw text of this value (null for SQL NULL); package-visible for binding tooling. */ 062 String text() { 063 return text; 064 } 065 066 public boolean isNull() { 067 return kind == Kind.NULL; 068 } 069 070 public boolean isNumber() { 071 return kind == Kind.NUMBER; 072 } 073 074 /** The string form of the value (null only when {@link #isNull()}). */ 075 public String asText() { 076 return text; 077 } 078 079 /** The numeric value, or null when this is not a number binding. */ 080 public Double asNumber() { 081 return number; 082 } 083 084 static String formatNumber(double value) { 085 if (value == Math.rint(value) && !Double.isInfinite(value)) { 086 return Long.toString((long) value); 087 } 088 return Double.toString(value); 089 } 090 091 @Override 092 public String toString() { 093 switch (kind) { 094 case NULL: 095 return "NULL"; 096 case NUMBER: 097 return text; 098 default: 099 return "'" + text + "'"; 100 } 101 } 102}