001package gudusoft.gsqlparser.catalog.input.model; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006import java.util.Objects; 007 008/** 009 * Table constraint entry. Plan §6. 010 * 011 * <p>{@link #type()} is a string (PK / FK / UNIQUE / CHECK / NOT_NULL) so the 012 * model stays forward-compatible with vendor-specific constraint flavors.</p> 013 * 014 * <p><b>Foreign-key targets.</b> {@link #referencedTable()} and 015 * {@link #referencedColumns()} carry the FK target side and mirror 016 * {@link gudusoft.gsqlparser.nodes.TConstraint#getReferencedObject()} / 017 * {@link gudusoft.gsqlparser.nodes.TConstraint#getReferencedColumnList()} on 018 * the AST side. The model is permissive: both fields may be set on any 019 * constraint type, but {@link gudusoft.gsqlparser.catalog.input.CatalogModelValidator} 020 * enforces FK-specific semantics (required target table, matching column counts, 021 * resolvable target table + columns).</p> 022 * 023 * <p>{@link #referencedTable()} is a single string and may carry a qualified 024 * name (e.g. {@code "schema.table"}). It is the catalog-side counterpart of 025 * {@link gudusoft.gsqlparser.nodes.TObjectName#toString()}.</p> 026 */ 027public final class ConstraintModel { 028 029 private final String name; 030 private final String type; 031 private final List<String> columns; 032 private final String referencedTable; 033 private final List<String> referencedColumns; 034 035 private ConstraintModel(Builder b) { 036 if (b.type == null || b.type.isEmpty()) { 037 throw new IllegalArgumentException("ConstraintModel.type is required"); 038 } 039 this.name = b.name; 040 this.type = b.type; 041 this.columns = Collections.unmodifiableList(new ArrayList<String>(b.columns)); 042 this.referencedTable = b.referencedTable; 043 this.referencedColumns = 044 Collections.unmodifiableList(new ArrayList<String>(b.referencedColumns)); 045 } 046 047 public static Builder builder() { 048 return new Builder(); 049 } 050 051 public String name() { 052 return name; 053 } 054 055 /** PK / FK / UNIQUE / CHECK / NOT_NULL — string for forward compatibility. */ 056 public String type() { 057 return type; 058 } 059 060 public List<String> columns() { 061 return columns; 062 } 063 064 /** 065 * Target table for a foreign-key constraint, or {@code null} for non-FK 066 * constraints. May be a single name ({@code "orders"}) or qualified 067 * ({@code "shop.orders"} / {@code "catalog.shop.orders"}). Resolution is 068 * up to the validator / runtime; the model itself does not interpret dots. 069 */ 070 public String referencedTable() { 071 return referencedTable; 072 } 073 074 /** 075 * Target columns on the foreign-key {@link #referencedTable()}, ordered 076 * to align positionally with {@link #columns()}. Empty (never {@code null}) 077 * for non-FK constraints. 078 */ 079 public List<String> referencedColumns() { 080 return referencedColumns; 081 } 082 083 @Override 084 public boolean equals(Object o) { 085 if (this == o) return true; 086 if (!(o instanceof ConstraintModel)) return false; 087 ConstraintModel that = (ConstraintModel) o; 088 return Objects.equals(name, that.name) 089 && type.equals(that.type) 090 && columns.equals(that.columns) 091 && Objects.equals(referencedTable, that.referencedTable) 092 && referencedColumns.equals(that.referencedColumns); 093 } 094 095 @Override 096 public int hashCode() { 097 return Objects.hash(name, type, columns, referencedTable, referencedColumns); 098 } 099 100 @Override 101 public String toString() { 102 return "ConstraintModel{name=" + name + ", type=" + type 103 + ", columns=" + columns 104 + ", referencedTable=" + referencedTable 105 + ", referencedColumns=" + referencedColumns + '}'; 106 } 107 108 public static final class Builder { 109 110 private String name; 111 private String type; 112 private final List<String> columns = new ArrayList<String>(); 113 private String referencedTable; 114 private final List<String> referencedColumns = new ArrayList<String>(); 115 116 private Builder() { 117 } 118 119 public Builder name(String v) { 120 this.name = v; 121 return this; 122 } 123 124 public Builder type(String v) { 125 this.type = v; 126 return this; 127 } 128 129 public Builder addColumn(String v) { 130 if (v == null) { 131 throw new IllegalArgumentException("ConstraintModel column may not be null"); 132 } 133 this.columns.add(v); 134 return this; 135 } 136 137 public Builder referencedTable(String v) { 138 this.referencedTable = v; 139 return this; 140 } 141 142 public Builder addReferencedColumn(String v) { 143 if (v == null) { 144 throw new IllegalArgumentException( 145 "ConstraintModel referenced column may not be null"); 146 } 147 this.referencedColumns.add(v); 148 return this; 149 } 150 151 public ConstraintModel build() { 152 return new ConstraintModel(this); 153 } 154 } 155}