1 5 package org.h2.command.dml; 6 7 import java.sql.SQLException ; 8 import java.util.HashSet ; 9 10 import org.h2.engine.Constants; 11 import org.h2.engine.Session; 12 import org.h2.expression.Expression; 13 import org.h2.expression.ExpressionColumn; 14 import org.h2.expression.ExpressionVisitor; 15 import org.h2.expression.ValueExpression; 16 import org.h2.jdbc.JdbcSQLException; 17 import org.h2.message.Message; 18 import org.h2.result.LocalResult; 19 import org.h2.result.SortOrder; 20 import org.h2.table.Column; 21 import org.h2.table.ColumnResolver; 22 import org.h2.table.TableFilter; 23 import org.h2.util.ObjectArray; 24 import org.h2.util.StringUtils; 25 import org.h2.value.Value; 26 import org.h2.value.ValueInt; 27 28 public class SelectUnion extends Query { 29 public static final int UNION=0, UNION_ALL=1, EXCEPT=2, INTERSECT=3; 30 private int unionType; 31 private Query left, right; 32 private ObjectArray expressions; 33 private ObjectArray orderList; 34 private SortOrder sort; 35 private boolean distinct; 36 private boolean checkPrepared, checkInit; 37 private boolean isForUpdate; 38 39 public SelectUnion(Session session, Query query) { 40 super(session); 41 this.left = query; 42 } 43 44 public void setUnionType(int type) { 45 this.unionType = type; 46 } 47 48 public void setRight(Query select) throws JdbcSQLException { 49 right = select; 50 } 51 52 53 54 public void setSQL(String sql) { 55 this.sql = sql; 56 } 57 58 public void setOrder(ObjectArray order) { 59 orderList = order; 60 } 61 62 private Value[] convert(Value[] values) throws SQLException { 63 for(int i=0; i<values.length; i++) { 64 Expression e = (Expression) expressions.get(i); 65 values[i] = values[i].convertTo(e.getType()); 66 } 67 return values; 68 } 69 70 public LocalResult queryWithoutCache(int maxrows) throws SQLException { 71 if(maxrows != 0) { 72 if(limit != null) { 73 maxrows = Math.min(limit.getValue(session).getInt(), maxrows); 74 } 75 limit = ValueExpression.get(ValueInt.get(maxrows)); 76 } 77 ObjectArray expressions = left.getExpressions(); 78 int columnCount = left.getColumnCount(); 79 LocalResult result = new LocalResult(session, expressions, columnCount); 80 result.setSortOrder(sort); 81 if(distinct) { 82 left.setDistinct(true); 83 right.setDistinct(true); 84 result.setDistinct(); 85 } 86 switch(unionType) { 87 case UNION: 88 left.setDistinct(true); 89 right.setDistinct(true); 90 result.setDistinct(); 91 break; 92 case UNION_ALL: 93 break; 94 case EXCEPT: 95 result.setDistinct(); 96 case INTERSECT: { 98 left.setDistinct(true); 99 right.setDistinct(true); 100 break; 101 } 102 default: 103 throw Message.getInternalError("type="+unionType); 104 } 105 LocalResult l = left.query(0); 106 LocalResult r = right.query(0); 107 l.reset(); 108 r.reset(); 109 switch(unionType) { 110 case UNION_ALL: 111 case UNION: { 112 while(l.next()) { 113 result.addRow(convert(l.currentRow())); 114 } 115 while(r.next()) { 116 result.addRow(convert(r.currentRow())); 117 } 118 break; 119 } 120 case EXCEPT: { 121 while(l.next()) { 122 result.addRow(convert(l.currentRow())); 123 } 124 while(r.next()) { 125 result.removeDistinct(convert(r.currentRow())); 126 } 127 break; 128 } 129 case INTERSECT: { 130 LocalResult temp = new LocalResult(session, expressions, columnCount); 131 temp.setDistinct(); 132 while(l.next()) { 133 temp.addRow(convert(l.currentRow())); 134 } 135 while(r.next()) { 136 Value[] values = convert(r.currentRow()); 137 if(temp.containsDistinct(values)) { 138 result.addRow(values); 139 } 140 } 141 break; 142 } 143 default: 144 throw Message.getInternalError("type="+unionType); 145 } 146 if(offset != null) { 147 result.setOffset(offset.getValue(session).getInt()); 148 } 149 if(limit != null) { 150 result.setLimit(limit.getValue(session).getInt()); 151 } 152 result.done(); 153 return result; 154 } 155 156 public void init() throws SQLException { 157 if(Constants.CHECK && checkInit) { 158 throw Message.getInternalError(); 159 } 160 checkInit = true; 161 left.init(); 162 right.init(); 163 int len = left.getColumnCount(); 164 if(len != right.getColumnCount()) { 165 throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH); 166 } 167 } 168 169 public void prepare() throws SQLException { 170 if(Constants.CHECK && (checkPrepared || !checkInit)) { 171 throw Message.getInternalError("already prepared"); 172 } 173 checkPrepared = true; 174 left.prepare(); 175 right.prepare(); 176 ObjectArray le = left.getExpressions(); 177 ObjectArray re = right.getExpressions(); 178 expressions = new ObjectArray(); 179 int len = left.getColumnCount(); 180 for(int i=0; i<len; i++) { 181 Expression l = (Expression)le.get(i); 182 Expression r = (Expression)re.get(i); 183 int type = Value.getHigherOrder(l.getType(), r.getType()); 184 long prec = Math.max(l.getPrecision(), r.getPrecision()); 185 int scale = Math.max(l.getScale(), r.getScale()); 186 Column col = new Column(l.getAlias(), type, prec, scale); 187 Expression e = new ExpressionColumn(session.getDatabase(), null, col); 188 expressions.add(e); 189 } 190 if(orderList != null) { 191 sort = initOrder(expressions, orderList, getColumnCount(), true); 192 orderList = null; 193 } 194 } 195 196 public double getCost() { 197 return left.getCost() + right.getCost(); 198 } 199 200 public HashSet getTables() { 201 HashSet set = left.getTables(); 202 set.addAll(right.getTables()); 203 return set; 204 } 205 206 public void setDistinct(boolean b) { 207 distinct = b; 208 } 209 210 public ObjectArray getExpressions() { 211 return expressions; 212 } 213 214 public void setForUpdate(boolean forUpdate) { 215 left.setForUpdate(forUpdate); 216 right.setForUpdate(forUpdate); 217 isForUpdate = forUpdate; 218 } 219 220 public int getColumnCount() { 221 return left.getColumnCount(); 222 } 223 224 public void mapColumns(ColumnResolver resolver, int level) throws SQLException { 225 left.mapColumns(resolver, level); 226 right.mapColumns(resolver, level); 227 } 228 229 public void setEvaluatable(TableFilter tableFilter, boolean b) { 230 left.setEvaluatable(tableFilter, b); 231 right.setEvaluatable(tableFilter, b); 232 } 233 234 public void addGlobalCondition(Expression expr, int columnId, int comparisonType) throws SQLException { 235 switch(unionType) { 236 case UNION_ALL: 237 case UNION: 238 case INTERSECT: { 239 left.addGlobalCondition(expr, columnId, comparisonType); 240 right.addGlobalCondition(expr, columnId, comparisonType); 241 break; 242 } 243 case EXCEPT: { 244 left.addGlobalCondition(expr, columnId, comparisonType); 245 break; 246 } 247 default: 248 throw Message.getInternalError("type="+unionType); 249 } 250 } 251 252 public String getPlan() { 253 StringBuffer buff = new StringBuffer (); 254 buff.append('('); 255 buff.append(left.getPlan()); 256 buff.append(") "); 257 switch(unionType) { 258 case UNION_ALL: 259 buff.append("UNION ALL "); 260 break; 261 case UNION: 262 buff.append("UNION "); 263 break; 264 case INTERSECT: 265 buff.append("INTERSECT "); 266 break; 267 case EXCEPT: 268 buff.append("EXCEPT "); 269 break; 270 default: 271 throw Message.getInternalError("type="+unionType); 272 } 273 buff.append('('); 274 buff.append(right.getPlan()); 275 buff.append(')'); 276 Expression[] exprList = new Expression[expressions.size()]; 277 expressions.toArray(exprList); 278 if(sort != null) { 279 buff.append(" ORDER BY "); 280 buff.append(sort.getSQL(exprList, exprList.length)); 281 } 282 if(limit != null) { 284 buff.append(" LIMIT "); 285 buff.append(StringUtils.unEnclose(limit.getSQL())); 286 if(offset != null) { 287 buff.append(" OFFSET "); 288 buff.append(StringUtils.unEnclose(offset.getSQL())); 289 } 290 } 291 if(isForUpdate) { 292 buff.append(" FOR UPDATE"); 293 } 294 return buff.toString(); 295 } 296 297 public boolean isEverything(ExpressionVisitor visitor) { 298 return left.isEverything(visitor) && right.isEverything(visitor); 299 } 300 301 public boolean isReadOnly() { 302 return left.isReadOnly() && right.isReadOnly(); 303 } 304 305 } 306 | Popular Tags |