001package gudusoft.gsqlparser.demos.sqlguard; 002 003import gudusoft.gsqlparser.util.json.JSON; 004 005import java.util.LinkedHashMap; 006import java.util.Map; 007 008/** Request POJO for the lightweight LLM SQL Guard worker. */ 009@SuppressWarnings("unchecked") 010public class SqlGuardRequest { 011 public String requestId; 012 public String dialect = "postgresql"; 013 public String sql; 014 public String catalogMode = "sample"; 015 public String sampleCatalogId = "ecommerce"; 016 public String catalog; 017 public String policyPreset = "pii-basic"; 018 /** 019 * Optional natural-language question that motivated the SQL — the 020 * caller's stated intent. Consumed by blueprint §S23 021 * ({@code AGGREGATION_GRAIN_MISMATCH}) to compare against the SQL's 022 * actual aggregation grain. Deliberately kept as a free-text string; 023 * the rule uses a small keyword whitelist, not NLP. {@code null} 024 * disables every intent-vs-SQL check (existing requests round-trip 025 * byte-identical). 026 */ 027 public String question; 028 public Map<String, Object> userContext = new LinkedHashMap<String, Object>(); 029 public Map<String, Object> options = new LinkedHashMap<String, Object>(); 030 031 public static SqlGuardRequest fromJson(String json) { 032 Object parsed = JSON.parseObject(json); 033 if (!(parsed instanceof Map)) { 034 throw new IllegalArgumentException("request body must be a JSON object"); 035 } 036 Map<String, Object> m = (Map<String, Object>) parsed; 037 SqlGuardRequest r = new SqlGuardRequest(); 038 r.requestId = optionalString(m, "requestId"); 039 String dialect = optionalString(m, "dialect"); 040 if (dialect != null) r.dialect = dialect; 041 r.sql = optionalString(m, "sql"); 042 String catalogMode = optionalString(m, "catalogMode"); 043 if (catalogMode != null) r.catalogMode = catalogMode; 044 String sampleCatalogId = optionalString(m, "sampleCatalogId"); 045 if (sampleCatalogId != null) r.sampleCatalogId = sampleCatalogId; 046 r.catalog = optionalString(m, "catalog"); 047 String policyPreset = optionalString(m, "policyPreset"); 048 if (policyPreset != null) r.policyPreset = policyPreset; 049 r.question = optionalString(m, "question"); 050 Object uc = m.get("userContext"); 051 if (uc instanceof Map) r.userContext.putAll((Map<String, Object>) uc); 052 else if (uc != null) throw new IllegalArgumentException("userContext must be a JSON object"); 053 Object opt = m.get("options"); 054 if (opt instanceof Map) r.options.putAll((Map<String, Object>) opt); 055 else if (opt != null) throw new IllegalArgumentException("options must be a JSON object"); 056 return r; 057 } 058 059 private static String optionalString(Map<String, Object> m, String key) { 060 Object o = m.get(key); 061 if (o == null) { 062 return null; 063 } 064 if (!(o instanceof String)) { 065 throw new IllegalArgumentException(key + " must be a string"); 066 } 067 return (String) o; 068 } 069}