001package gudusoft.gsqlparser.resolver2.scope; 002 003import gudusoft.gsqlparser.nodes.TParseTreeNode; 004import gudusoft.gsqlparser.nodes.TJoinExpr; 005import gudusoft.gsqlparser.EJoinType; 006import gudusoft.gsqlparser.resolver2.ScopeType; 007import gudusoft.gsqlparser.resolver2.namespace.INamespace; 008 009/** 010 * Scope for JOIN operations. 011 * Handles nullable semantics for different JOIN types. 012 * 013 * JOIN semantics: 014 * - INNER JOIN: both sides non-nullable 015 * - LEFT JOIN: left non-nullable, right nullable 016 * - RIGHT JOIN: left nullable, right non-nullable 017 * - FULL JOIN: both sides nullable 018 * - CROSS JOIN: both sides non-nullable 019 * 020 * Example: 021 * FROM users u 022 * LEFT JOIN orders o ON u.id = o.user_id 023 * ^^^^^ ^^^^^^ 024 * leftNamespace rightNamespace (nullable) 025 */ 026public class JoinScope extends ListBasedScope { 027 028 /** The JOIN AST node */ 029 private final TJoinExpr joinExpr; 030 031 /** Left side namespace */ 032 private INamespace leftNamespace; 033 034 /** Right side namespace */ 035 private INamespace rightNamespace; 036 037 /** Whether left side is nullable */ 038 private boolean leftNullable; 039 040 /** Whether right side is nullable */ 041 private boolean rightNullable; 042 043 public JoinScope(IScope parent, TJoinExpr joinExpr) { 044 super(parent, joinExpr, ScopeType.JOIN); 045 this.joinExpr = joinExpr; 046 } 047 048 /** 049 * Set the left side of the JOIN 050 */ 051 public void setLeft(INamespace namespace, String alias, boolean nullable) { 052 this.leftNamespace = namespace; 053 this.leftNullable = nullable; 054 addChild(namespace, alias, nullable); 055 } 056 057 /** 058 * Set the right side of the JOIN 059 */ 060 public void setRight(INamespace namespace, String alias, boolean nullable) { 061 this.rightNamespace = namespace; 062 this.rightNullable = nullable; 063 addChild(namespace, alias, nullable); 064 } 065 066 /** 067 * Determine nullable semantics based on JOIN type 068 */ 069 public static NullableSemantics getNullableSemantics(TJoinExpr joinExpr) { 070 if (joinExpr == null || joinExpr.getJointype() == null) { 071 return new NullableSemantics(false, false); 072 } 073 074 EJoinType joinType = joinExpr.getJointype(); 075 076 switch (joinType) { 077 case inner: 078 case join: // same as inner join 079 case natural_inner: 080 return new NullableSemantics(false, false); 081 082 case left: 083 case leftouter: 084 case natural_left: 085 case natural_leftouter: 086 case leftsemi: 087 return new NullableSemantics(false, true); 088 089 case right: 090 case rightouter: 091 case natural_right: 092 case natural_rightouter: 093 return new NullableSemantics(true, false); 094 095 case full: 096 case fullouter: 097 case natural_full: 098 case natural_fullouter: 099 return new NullableSemantics(true, true); 100 101 case cross: 102 case straight: 103 case crossapply: 104 return new NullableSemantics(false, false); 105 106 case outerapply: 107 return new NullableSemantics(false, true); 108 109 case leftanti: 110 case anti: 111 case semi: 112 return new NullableSemantics(false, true); 113 114 case asof: 115 return new NullableSemantics(false, true); 116 117 default: 118 // Conservative default: assume both nullable 119 return new NullableSemantics(true, true); 120 } 121 } 122 123 public TJoinExpr getJoinExpr() { 124 return joinExpr; 125 } 126 127 public INamespace getLeftNamespace() { 128 return leftNamespace; 129 } 130 131 public INamespace getRightNamespace() { 132 return rightNamespace; 133 } 134 135 public boolean isLeftNullable() { 136 return leftNullable; 137 } 138 139 public boolean isRightNullable() { 140 return rightNullable; 141 } 142 143 @Override 144 public String toString() { 145 return String.format("JoinScope(left=%s[%s], right=%s[%s])", 146 leftNamespace != null ? leftNamespace.getDisplayName() : "null", 147 leftNullable ? "nullable" : "not-null", 148 rightNamespace != null ? rightNamespace.getDisplayName() : "null", 149 rightNullable ? "nullable" : "not-null" 150 ); 151 } 152 153 /** 154 * Nullable semantics for a JOIN operation 155 */ 156 public static class NullableSemantics { 157 public final boolean leftNullable; 158 public final boolean rightNullable; 159 160 public NullableSemantics(boolean leftNullable, boolean rightNullable) { 161 this.leftNullable = leftNullable; 162 this.rightNullable = rightNullable; 163 } 164 } 165}