1 /* 2 * Copyright 2015-2018 HuntLabs.cn 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 module hunt.sql.ast.statement.SQLJoinTableSource; 17 18 19 import hunt.collection; 20 21 import hunt.sql.SQLUtils; 22 import hunt.sql.ast.SQLExpr; 23 import hunt.sql.ast.SQLReplaceable; 24 import hunt.sql.ast.expr.SQLBinaryOpExpr; 25 import hunt.sql.ast.expr.SQLIdentifierExpr; 26 import hunt.sql.ast.expr.SQLPropertyExpr; 27 import hunt.sql.visitor.SQLASTVisitor; 28 import hunt.sql.util.FnvHash; 29 import hunt.sql.ast.statement.SQLExprTableSource; 30 import hunt.sql.ast.statement.SQLTableSourceImpl; 31 import hunt.sql.ast.statement.SQLTableSource; 32 import hunt.sql.ast.statement.SQLColumnDefinition; 33 import hunt.util.StringBuilder; 34 import std.uni; 35 import hunt.sql.ast.SQLObject; 36 37 public class SQLJoinTableSource : SQLTableSourceImpl , SQLReplaceable { 38 39 protected SQLTableSource left; 40 protected JoinType joinType; 41 protected SQLTableSource right; 42 protected SQLExpr condition; 43 protected List!SQLExpr _using; 44 45 46 protected bool natural = false; 47 48 public this(string alias_p){ 49 _using = new ArrayList!SQLExpr(); 50 super(alias_p); 51 } 52 53 public this(){ 54 _using = new ArrayList!SQLExpr(); 55 } 56 57 public this(SQLTableSource left, JoinType joinType, SQLTableSource right, SQLExpr condition){ 58 this(); 59 this.setLeft(left); 60 this.setJoinType(joinType); 61 this.setRight(right); 62 this.setCondition(condition); 63 } 64 65 public this(SQLTableSource left, JoinType joinType, SQLTableSource right){ 66 this(); 67 this.setLeft(left); 68 this.setJoinType(joinType); 69 this.setRight(right); 70 } 71 72 override protected void accept0(SQLASTVisitor visitor) { 73 if (visitor.visit(this)) { 74 acceptChild(visitor, this.left); 75 acceptChild(visitor, this.right); 76 acceptChild(visitor, this.condition); 77 acceptChild!SQLExpr(visitor, this._using); 78 } 79 80 visitor.endVisit(this); 81 } 82 83 public JoinType getJoinType() { 84 return this.joinType; 85 } 86 87 public void setJoinType(JoinType joinType) { 88 this.joinType = joinType; 89 } 90 91 public SQLTableSource getLeft() { 92 return this.left; 93 } 94 95 public void setLeft(SQLTableSource left) { 96 if (left !is null) { 97 left.setParent(this); 98 } 99 this.left = left; 100 } 101 102 public void setLeft(string tableName, string alias_p) { 103 SQLExprTableSource tableSource; 104 if (tableName is null || tableName.length == 0) { 105 tableSource = null; 106 } else { 107 tableSource = new SQLExprTableSource(new SQLIdentifierExpr(tableName), alias_p); 108 } 109 this.setLeft(tableSource); 110 } 111 112 public void setRight(string tableName, string alias_p) { 113 SQLExprTableSource tableSource; 114 if (tableName is null || tableName.length == 0) { 115 tableSource = null; 116 } else { 117 tableSource = new SQLExprTableSource(new SQLIdentifierExpr(tableName), alias_p); 118 } 119 this.setRight(tableSource); 120 } 121 122 public SQLTableSource getRight() { 123 return this.right; 124 } 125 126 public void setRight(SQLTableSource right) { 127 if (right !is null) { 128 right.setParent(this); 129 } 130 this.right = right; 131 } 132 133 public SQLExpr getCondition() { 134 return this.condition; 135 } 136 137 public void setCondition(SQLExpr condition) { 138 if (condition !is null) { 139 condition.setParent(this); 140 } 141 this.condition = condition; 142 } 143 144 public void addConditionn(SQLExpr condition) { 145 this.condition = SQLBinaryOpExpr.and(this.condition, condition); 146 } 147 148 public void addConditionnIfAbsent(SQLExpr condition) { 149 if (this.containsCondition(condition)) { 150 return; 151 } 152 this.condition = SQLBinaryOpExpr.and(this.condition, condition); 153 } 154 155 public bool containsCondition(SQLExpr condition) { 156 if (this.condition is null) { 157 return false; 158 } 159 160 if ((cast(Object)(this.condition)).opEquals(cast(Object)(condition))) { 161 return false; 162 } 163 164 if (cast(SQLBinaryOpExpr)(this.condition) !is null ) { 165 return (cast(SQLBinaryOpExpr) this.condition).contains(condition); 166 } 167 168 return false; 169 } 170 171 public List!SQLExpr getUsing() { 172 return this._using; 173 } 174 175 public bool isNatural() { 176 return natural; 177 } 178 179 public void setNatural(bool natural) { 180 this.natural = natural; 181 } 182 183 override public void output(StringBuilder buf) { 184 this.left.output(buf); 185 buf.append(' '); 186 buf.append(JoinType.toString(this.joinType)); 187 buf.append(' '); 188 this.right.output(buf); 189 190 if (this.condition !is null) { 191 buf.append(" ON "); 192 this.condition.output(buf); 193 } 194 } 195 196 override 197 public bool opEquals(Object o) { 198 if (this == o) return true; 199 if (o is null || typeid(this) != typeid(o)) return false; 200 201 SQLJoinTableSource that = cast(SQLJoinTableSource) o; 202 203 if (natural != that.natural) return false; 204 if (left !is null ? !(cast(Object)(left)).opEquals(cast(Object)(that.left)) : that.left !is null) return false; 205 if (joinType != that.joinType) return false; 206 if (right !is null ? !(cast(Object)(right)).opEquals(cast(Object)(that.right)) : that.right !is null) return false; 207 if (condition !is null ? !(cast(Object)(condition)).opEquals(cast(Object)(that.condition)) : that.condition !is null) return false; 208 return _using !is null ? (cast(Object)(_using)).opEquals(cast(Object)(that._using)) : that._using is null; 209 } 210 211 override 212 public bool replace(SQLExpr expr, SQLExpr target) { 213 if (condition == expr) { 214 setCondition(target); 215 return true; 216 } 217 218 return false; 219 } 220 221 public static struct JoinType { 222 enum JoinType COMMA = JoinType(","); // 223 enum JoinType JOIN = JoinType("JOIN"); // 224 enum JoinType INNER_JOIN = JoinType("INNER JOIN"); // 225 enum JoinType CROSS_JOIN = JoinType("CROSS JOIN"); // 226 enum JoinType NATURAL_JOIN = JoinType("NATURAL JOIN"); // 227 enum JoinType NATURAL_INNER_JOIN = JoinType("NATURAL INNER JOIN"); // 228 enum JoinType LEFT_OUTER_JOIN = JoinType("LEFT JOIN"); // 229 enum JoinType LEFT_SEMI_JOIN = JoinType("LEFT SEMI JOIN"); // 230 enum JoinType LEFT_ANTI_JOIN = JoinType("LEFT ANTI JOIN"); // 231 enum JoinType RIGHT_OUTER_JOIN = JoinType("RIGHT JOIN"); // 232 enum JoinType FULL_OUTER_JOIN = JoinType("FULL JOIN");// 233 enum JoinType STRAIGHT_JOIN = JoinType("STRAIGHT_JOIN"); // 234 enum JoinType OUTER_APPLY = JoinType("OUTER APPLY");// 235 enum JoinType CROSS_APPLY = JoinType("CROSS APPLY"); 236 237 public string name; 238 public string name_lcase; 239 240 this(string name){ 241 this.name = name; 242 this.name_lcase = toLower(name); 243 } 244 245 public static string toString(JoinType joinType) { 246 return joinType.name; 247 } 248 249 bool opEquals(const JoinType h) nothrow { 250 return name == h.name ; 251 } 252 253 bool opEquals(ref const JoinType h) nothrow { 254 return name == h.name ; 255 } 256 } 257 258 259 public void cloneTo(SQLJoinTableSource x) { 260 x._alias = _alias; 261 262 if (left !is null) { 263 x.setLeft(left.clone()); 264 } 265 266 x.joinType = joinType; 267 268 if (right !is null) { 269 x.setRight(right.clone()); 270 } 271 272 if(condition !is null){ 273 x.setCondition(condition); 274 } 275 276 foreach (SQLExpr item ; _using) { 277 SQLExpr item2 = item.clone(); 278 item2.setParent(x); 279 x._using.add(item2); 280 } 281 282 x.natural = natural; 283 } 284 285 override public SQLJoinTableSource clone() { 286 SQLJoinTableSource x = new SQLJoinTableSource(); 287 cloneTo(x); 288 return x; 289 } 290 291 public void reverse() { 292 SQLTableSource temp = left; 293 left = right; 294 right = temp; 295 296 if (cast(SQLJoinTableSource)(left) !is null ) { 297 (cast(SQLJoinTableSource) left).reverse(); 298 } 299 300 if (cast(SQLJoinTableSource)(right) !is null ) { 301 (cast(SQLJoinTableSource) right).reverse(); 302 } 303 } 304 305 /** 306 * a inner_join (b inner_join c) -< a inner_join b innre_join c 307 */ 308 public void rearrangement() { 309 if (joinType != JoinType.COMMA && joinType != JoinType.INNER_JOIN) { 310 return; 311 } 312 if (cast(SQLJoinTableSource)(right) !is null ) { 313 SQLJoinTableSource rightJoin = cast(SQLJoinTableSource) right; 314 315 if (rightJoin.joinType != JoinType.COMMA && rightJoin.joinType != JoinType.INNER_JOIN) { 316 return; 317 } 318 319 SQLTableSource a = left; 320 SQLTableSource b = rightJoin.getLeft(); 321 SQLTableSource c = rightJoin.getRight(); 322 SQLExpr on_ab = condition; 323 SQLExpr on_bc = rightJoin.condition; 324 325 setLeft(rightJoin); 326 rightJoin.setLeft(a); 327 rightJoin.setRight(b); 328 329 330 bool on_ab_match = false; 331 if (cast(SQLBinaryOpExpr)(on_ab) !is null ) { 332 SQLBinaryOpExpr on_ab_binaryOpExpr = cast(SQLBinaryOpExpr) on_ab; 333 if ( cast(SQLPropertyExpr)(on_ab_binaryOpExpr.getLeft()) !is null 334 && (cast(SQLPropertyExpr)on_ab_binaryOpExpr.getRight()) !is null ) { 335 string leftOwnerName = (cast(SQLPropertyExpr) on_ab_binaryOpExpr.getLeft()).getOwnernName(); 336 string rightOwnerName = (cast(SQLPropertyExpr) on_ab_binaryOpExpr.getRight()).getOwnernName(); 337 338 if (rightJoin.containsAlias(leftOwnerName) && rightJoin.containsAlias(rightOwnerName)) { 339 on_ab_match = true; 340 } 341 } 342 } 343 344 if (on_ab_match) { 345 rightJoin.setCondition(on_ab); 346 } else { 347 rightJoin.setCondition(null); 348 on_bc = SQLBinaryOpExpr.and(on_bc, on_ab); 349 } 350 351 setRight(c); 352 setCondition(on_bc); 353 } 354 } 355 356 public bool contains(SQLTableSource tableSource, SQLExpr condition) { 357 if ((cast(Object)(right)).opEquals(cast(Object)(tableSource))) { 358 if (this.condition == condition) { 359 return true; 360 } 361 362 return this.condition !is null && (cast(Object)(this.condition)).opEquals(cast(Object)(condition)); 363 } 364 365 if (cast(SQLJoinTableSource)(left) !is null ) { 366 SQLJoinTableSource joinLeft = cast(SQLJoinTableSource) left; 367 368 if (cast(SQLJoinTableSource)(tableSource) !is null ) { 369 SQLJoinTableSource join = cast(SQLJoinTableSource) tableSource; 370 371 if ((cast(Object)(join.right)).opEquals(cast(Object)(right)) && (cast(Object)(this.condition)).opEquals(cast(Object)(condition)) && (cast(Object)(joinLeft.right)).opEquals(cast(Object)(join.left))) { 372 return true; 373 } 374 } 375 376 return joinLeft.contains(tableSource, condition); 377 } 378 379 return false; 380 } 381 382 public bool contains(SQLTableSource tableSource, SQLExpr condition, JoinType joinType) { 383 if ((cast(Object)(right)).opEquals(cast(Object)(tableSource))) { 384 if (this.condition == condition) { 385 return true; 386 } 387 388 return this.condition !is null && (cast(Object)(this.condition)).opEquals(cast(Object)(condition)) && this.joinType == joinType; 389 } 390 391 if (cast(SQLJoinTableSource)(left) !is null ) { 392 SQLJoinTableSource joinLeft = cast(SQLJoinTableSource) left; 393 394 if (cast(SQLJoinTableSource)(tableSource) !is null ) { 395 SQLJoinTableSource join = cast(SQLJoinTableSource) tableSource; 396 397 if ((cast(Object)(join.right)).opEquals(cast(Object)(right)) 398 && this.condition !is null && (cast(Object)(this.condition)).opEquals(cast(Object)(join.condition)) 399 && (cast(Object)(joinLeft.right)).opEquals(cast(Object)(join.left)) 400 && this.joinType == join.joinType 401 && joinLeft.condition !is null && (cast(Object)(joinLeft.condition)).opEquals(cast(Object)(condition)) 402 && joinLeft.joinType == joinType) { 403 return true; 404 } 405 } 406 407 return joinLeft.contains(tableSource, condition, joinType); 408 } 409 410 return false; 411 } 412 413 public SQLJoinTableSource findJoin(SQLTableSource tableSource, JoinType joinType) { 414 if ((cast(Object)(right)).opEquals(cast(Object)(tableSource))) { 415 if (this.joinType == joinType) { 416 return this; 417 } 418 return null; 419 } 420 421 if (cast(SQLJoinTableSource)(left) !is null ) { 422 return (cast(SQLJoinTableSource) left).findJoin(tableSource, joinType); 423 } 424 425 return null; 426 } 427 428 override public bool containsAlias(string alias_p) { 429 if (SQLUtils.nameEquals(this._alias, alias_p)) { 430 return true; 431 } 432 433 if (left !is null && left.containsAlias(alias_p)) { 434 return true; 435 } 436 437 if (right !is null && right.containsAlias(alias_p)) { 438 return true; 439 } 440 441 return false; 442 } 443 444 override public SQLColumnDefinition findColumn(string columnName) { 445 long hash = FnvHash.hashCode64(columnName); 446 return findColumn(hash); 447 } 448 449 override public SQLColumnDefinition findColumn(long columnNameHash) { 450 if (left !is null) { 451 SQLColumnDefinition column = left.findColumn(columnNameHash); 452 if (column !is null) { 453 return column; 454 } 455 } 456 457 if (right !is null) { 458 return right.findColumn(columnNameHash); 459 } 460 461 return null; 462 } 463 464 override 465 public SQLTableSource findTableSourceWithColumn(string columnName) { 466 long hash = FnvHash.hashCode64(columnName); 467 return findTableSourceWithColumn(hash); 468 } 469 470 override public SQLTableSource findTableSourceWithColumn(long columnNameHash) { 471 if (left !is null) { 472 SQLTableSource tableSource = left.findTableSourceWithColumn(columnNameHash); 473 if (tableSource !is null) { 474 return tableSource; 475 } 476 } 477 478 if (right !is null) { 479 return right.findTableSourceWithColumn(columnNameHash); 480 } 481 482 return null; 483 } 484 485 public bool match(string alias_a, string alias_b) { 486 if (left is null || right is null) { 487 return false; 488 } 489 490 if (left.containsAlias(alias_a) 491 && right.containsAlias(alias_b)) { 492 return true; 493 } 494 495 return right.containsAlias(alias_a) 496 && left.containsAlias(alias_b); 497 } 498 499 public bool conditionContainsTable(string alias_p) { 500 if (condition is null) { 501 return false; 502 } 503 504 if (cast(SQLBinaryOpExpr)(condition) !is null ) { 505 return (cast(SQLBinaryOpExpr) condition).conditionContainsTable(alias_p); 506 } 507 508 return false; 509 } 510 511 public SQLJoinTableSource join(SQLTableSource right, JoinType joinType, SQLExpr condition) { 512 SQLJoinTableSource joined = new SQLJoinTableSource(this, joinType, right, condition); 513 return joined; 514 } 515 516 override public SQLTableSource findTableSource(long alias_hash) { 517 if (alias_hash == 0) { 518 return null; 519 } 520 521 if (aliasHashCode64() == alias_hash) { 522 return this; 523 } 524 525 SQLTableSource result = left.findTableSource(alias_hash); 526 if (result !is null) { 527 return result; 528 } 529 530 return right.findTableSource(alias_hash); 531 } 532 533 public SQLTableSource other(SQLTableSource x) { 534 if (left == x) { 535 return right; 536 } 537 538 if (right == x) { 539 return left; 540 } 541 542 return null; 543 } 544 } 545 546 // public struct JoinType { 547 // enum JoinType COMMA = JoinType(","); // 548 // enum JoinType JOIN = JoinType("JOIN"); // 549 // enum JoinType INNER_JOIN = JoinType("INNER JOIN"); // 550 // enum JoinType CROSS_JOIN = JoinType("CROSS JOIN"); // 551 // enum JoinType NATURAL_JOIN = JoinType("NATURAL JOIN"); // 552 // enum JoinType NATURAL_INNER_JOIN = JoinType("NATURAL INNER JOIN"); // 553 // enum JoinType LEFT_OUTER_JOIN = JoinType("LEFT JOIN"); // 554 // enum JoinType LEFT_SEMI_JOIN = JoinType("LEFT SEMI JOIN"); // 555 // enum JoinType LEFT_ANTI_JOIN = JoinType("LEFT ANTI JOIN"); // 556 // enum JoinType RIGHT_OUTER_JOIN = JoinType("RIGHT JOIN"); // 557 // enum JoinType FULL_OUTER_JOIN = JoinType("FULL JOIN");// 558 // enum JoinType STRAIGHT_JOIN = JoinType("STRAIGHT_JOIN"); // 559 // enum JoinType OUTER_APPLY = JoinType("OUTER APPLY");// 560 // enum JoinType CROSS_APPLY = JoinType("CROSS APPLY"); 561 562 // public string name; 563 // public string name_lcase; 564 565 // this(string name){ 566 // this.name = name; 567 // this.name_lcase = toLower(name); 568 // } 569 570 // public static string toString(JoinType joinType) { 571 // return joinType.name; 572 // } 573 574 // bool opEquals(const JoinType h) nothrow { 575 // return name == h.name ; 576 // } 577 578 // bool opEquals(ref const JoinType h) nothrow { 579 // return name == h.name ; 580 // } 581 // }