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.dialect.mysql.parser.MySqlSelectParser; 17 18 import hunt.sql.ast.SQLExpr; 19 import hunt.sql.ast.SQLName; 20 import hunt.sql.ast.SQLObject; 21 import hunt.sql.ast.SQLSetQuantifier; 22 import hunt.sql.ast.expr.SQLIdentifierExpr; 23 import hunt.sql.ast.expr.SQLListExpr; 24 import hunt.sql.ast.expr.SQLLiteralExpr; 25 import hunt.sql.ast.statement; 26 import hunt.sql.dialect.mysql.ast.MySqlForceIndexHint; 27 import hunt.sql.dialect.mysql.ast.MySqlIgnoreIndexHint; 28 import hunt.sql.dialect.mysql.ast.MySqlIndexHint; 29 import hunt.sql.dialect.mysql.ast.MySqlIndexHintImpl; 30 import hunt.sql.dialect.mysql.ast.MySqlUseIndexHint; 31 import hunt.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; 32 import hunt.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; 33 import hunt.sql.dialect.mysql.ast.statement.MySqlUpdateStatement; 34 import hunt.sql.dialect.mysql.ast.statement.MySqlUpdateTableSource; 35 import hunt.sql.parser; 36 import hunt.sql.util.FnvHash; 37 import hunt.sql.ast.statement.SQLSelectQuery; 38 import hunt.sql.dialect.mysql.parser.MySqlExprParser; 39 import hunt.Boolean; 40 import hunt.collection; 41 import hunt.sql.ast.SQLCommentHint; 42 import hunt.sql.ast.SQLHint; 43 44 public class MySqlSelectParser : SQLSelectParser { 45 46 public bool returningFlag = false; 47 public MySqlUpdateStatement updateStmt; 48 49 public this(SQLExprParser exprParser){ 50 super(exprParser); 51 } 52 53 public this(SQLExprParser exprParser, SQLSelectListCache selectListCache){ 54 super(exprParser, selectListCache); 55 } 56 57 public this(string sql){ 58 this(new MySqlExprParser(sql)); 59 } 60 61 override public void parseFrom(SQLSelectQueryBlock queryBlock) { 62 if (lexer.token() != Token.FROM) { 63 return; 64 } 65 66 lexer.nextTokenIdent(); 67 68 if (lexer.token() == Token.UPDATE) { // taobao returning to urgly syntax 69 updateStmt = this.parseUpdateStatment(); 70 List!(SQLExpr) returnning = updateStmt.getReturning(); 71 foreach(SQLSelectItem item ; queryBlock.getSelectList()) { 72 SQLExpr itemExpr = item.getExpr(); 73 itemExpr.setParent(updateStmt); 74 returnning.add(itemExpr); 75 } 76 returningFlag = true; 77 return; 78 } 79 80 queryBlock.setFrom(parseTableSource()); 81 } 82 83 84 override 85 public SQLSelectQuery query(SQLObject parent, bool acceptUnion) { 86 if (lexer.token() == Token.LPAREN) { 87 lexer.nextToken(); 88 89 SQLSelectQuery select = super.query(); //@gxc 90 select.setBracket(true); 91 accept(Token.RPAREN); 92 93 return queryRest(select, acceptUnion); 94 } 95 96 MySqlSelectQueryBlock queryBlock = new MySqlSelectQueryBlock(); 97 queryBlock.setParent(parent); 98 99 if (lexer.hasComment() && lexer.isKeepComments()) { 100 queryBlock.addBeforeComment(lexer.readAndResetComments()); 101 } 102 103 if (lexer.token() == Token.SELECT) { 104 if (selectListCache !is null) { 105 selectListCache.match(lexer, queryBlock); 106 } 107 } 108 109 if (lexer.token() == Token.SELECT) { 110 lexer.nextTokenValue(); 111 112 for(;;) { 113 if (lexer.token() == Token.HINT) { 114 this.exprParser.parseHints!(SQLCommentHint)((queryBlock.getHints())); 115 } else { 116 break; 117 } 118 } 119 120 Token token = lexer.token(); 121 if (token == (Token.DISTINCT)) { 122 queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); 123 lexer.nextToken(); 124 } else if (lexer.identifierEquals(FnvHash.Constants.DISTINCTROW)) { 125 queryBlock.setDistionOption(SQLSetQuantifier.DISTINCTROW); 126 lexer.nextToken(); 127 } else if (token == (Token.ALL)) { 128 queryBlock.setDistionOption(SQLSetQuantifier.ALL); 129 lexer.nextToken(); 130 } 131 132 if (lexer.identifierEquals(FnvHash.Constants.HIGH_PRIORITY)) { 133 queryBlock.setHignPriority(true); 134 lexer.nextToken(); 135 } 136 137 if (lexer.identifierEquals(FnvHash.Constants.STRAIGHT_JOIN)) { 138 queryBlock.setStraightJoin(true); 139 lexer.nextToken(); 140 } 141 142 if (lexer.identifierEquals(FnvHash.Constants.SQL_SMALL_RESULT)) { 143 queryBlock.setSmallResult(true); 144 lexer.nextToken(); 145 } 146 147 if (lexer.identifierEquals(FnvHash.Constants.SQL_BIG_RESULT)) { 148 queryBlock.setBigResult(true); 149 lexer.nextToken(); 150 } 151 152 if (lexer.identifierEquals(FnvHash.Constants.SQL_BUFFER_RESULT)) { 153 queryBlock.setBufferResult(true); 154 lexer.nextToken(); 155 } 156 157 if (lexer.identifierEquals(FnvHash.Constants.SQL_CACHE)) { 158 queryBlock.setCache(new Boolean(true)); 159 lexer.nextToken(); 160 } 161 162 if (lexer.identifierEquals(FnvHash.Constants.SQL_NO_CACHE)) { 163 queryBlock.setCache(new Boolean(false)); 164 lexer.nextToken(); 165 } 166 167 if (lexer.identifierEquals(FnvHash.Constants.SQL_CALC_FOUND_ROWS)) { 168 queryBlock.setCalcFoundRows(true); 169 lexer.nextToken(); 170 } 171 172 parseSelectList(queryBlock); 173 174 if (lexer.identifierEquals(FnvHash.Constants.FORCE)) { 175 lexer.nextToken(); 176 accept(Token.PARTITION); 177 SQLName partition = this.exprParser.name(); 178 queryBlock.setForcePartition(partition); 179 } 180 181 parseInto(queryBlock); 182 } 183 184 parseFrom(queryBlock); 185 186 parseWhere(queryBlock); 187 188 parseHierachical(queryBlock); 189 190 parseGroupBy(queryBlock); 191 192 queryBlock.setOrderBy(this.exprParser.parseOrderBy()); 193 194 if (lexer.token() == Token.LIMIT) { 195 queryBlock.setLimit(this.exprParser.parseLimit()); 196 } 197 198 if (lexer.token() == Token.PROCEDURE) { 199 lexer.nextToken(); 200 throw new ParserException("TODO. " ~ lexer.info()); 201 } 202 203 parseInto(queryBlock); 204 205 if (lexer.token() == Token.FOR) { 206 lexer.nextToken(); 207 accept(Token.UPDATE); 208 209 queryBlock.setForUpdate(true); 210 211 if (lexer.identifierEquals(FnvHash.Constants.NO_WAIT) || lexer.identifierEquals(FnvHash.Constants.NOWAIT)) { 212 lexer.nextToken(); 213 queryBlock.setNoWait(true); 214 } else if (lexer.identifierEquals(FnvHash.Constants.WAIT)) { 215 lexer.nextToken(); 216 SQLExpr waitTime = this.exprParser.primary(); 217 queryBlock.setWaitTime(waitTime); 218 } 219 } 220 221 if (lexer.token() == Token.LOCK) { 222 lexer.nextToken(); 223 accept(Token.IN); 224 acceptIdentifier("SHARE"); 225 acceptIdentifier("MODE"); 226 queryBlock.setLockInShareMode(true); 227 } 228 229 return queryRest(queryBlock, acceptUnion); 230 } 231 232 override public SQLTableSource parseTableSource() { 233 if (lexer.token() == Token.LPAREN) { 234 lexer.nextToken(); 235 SQLTableSource tableSource; 236 if (lexer.token() == Token.SELECT || lexer.token() == Token.WITH) { 237 SQLSelect select = select(); 238 239 accept(Token.RPAREN); 240 241 SQLSelectQuery query = queryRest(select.getQuery()); 242 if (cast(SQLUnionQuery)(query) !is null && select.getWithSubQuery() is null) { 243 select.getQuery().setBracket(true); 244 tableSource = new SQLUnionQueryTableSource(cast(SQLUnionQuery) query); 245 } else { 246 tableSource = new SQLSubqueryTableSource(select); 247 } 248 } else if (lexer.token() == Token.LPAREN) { 249 tableSource = parseTableSource(); 250 accept(Token.RPAREN); 251 } else { 252 tableSource = parseTableSource(); 253 accept(Token.RPAREN); 254 } 255 256 return parseTableSourceRest(tableSource); 257 } 258 259 if(lexer.token() == Token.UPDATE) { 260 SQLTableSource tableSource = new MySqlUpdateTableSource(parseUpdateStatment()); 261 return parseTableSourceRest(tableSource); 262 } 263 264 if (lexer.token() == Token.SELECT) { 265 throw new ParserException("TODO. " ~ lexer.info()); 266 } 267 268 SQLExprTableSource tableReference = new SQLExprTableSource(); 269 270 parseTableSourceQueryTableExpr(tableReference); 271 272 SQLTableSource tableSrc = parseTableSourceRest(tableReference); 273 274 if (lexer.hasComment() && lexer.isKeepComments()) { 275 tableSrc.addAfterComment(lexer.readAndResetComments()); 276 } 277 278 return tableSrc; 279 } 280 281 public MySqlUpdateStatement parseUpdateStatment() { 282 MySqlUpdateStatement update = new MySqlUpdateStatement(); 283 284 lexer.nextToken(); 285 286 if (lexer.identifierEquals(FnvHash.Constants.LOW_PRIORITY)) { 287 lexer.nextToken(); 288 update.setLowPriority(true); 289 } 290 291 if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) { 292 lexer.nextToken(); 293 update.setIgnore(true); 294 } 295 296 if (lexer.identifierEquals(FnvHash.Constants.COMMIT_ON_SUCCESS)) { 297 lexer.nextToken(); 298 update.setCommitOnSuccess(true); 299 } 300 301 if (lexer.identifierEquals(FnvHash.Constants.ROLLBACK_ON_FAIL)) { 302 lexer.nextToken(); 303 update.setRollBackOnFail(true); 304 } 305 306 if (lexer.identifierEquals(FnvHash.Constants.QUEUE_ON_PK)) { 307 lexer.nextToken(); 308 update.setQueryOnPk(true); 309 } 310 311 if (lexer.identifierEquals(FnvHash.Constants.TARGET_AFFECT_ROW)) { 312 lexer.nextToken(); 313 SQLExpr targetAffectRow = this.exprParser.expr(); 314 update.setTargetAffectRow(targetAffectRow); 315 } 316 317 if (lexer.identifierEquals(FnvHash.Constants.FORCE)) { 318 lexer.nextToken(); 319 320 if (lexer.token() == Token.ALL) { 321 lexer.nextToken(); 322 acceptIdentifier("PARTITIONS"); 323 update.setForceAllPartitions(true); 324 } else if (lexer.identifierEquals(FnvHash.Constants.PARTITIONS)){ 325 lexer.nextToken(); 326 update.setForceAllPartitions(true); 327 } else if (lexer.token() == Token.PARTITION) { 328 lexer.nextToken(); 329 SQLName partition = this.exprParser.name(); 330 update.setForcePartition(partition); 331 } else { 332 throw new ParserException("TODO. " ~ lexer.info()); 333 } 334 } 335 336 while (lexer.token() == Token.HINT) { 337 this.exprParser.parseHints!(SQLHint)((update.getHints())); 338 } 339 340 SQLSelectParser selectParser = this.exprParser.createSelectParser(); 341 SQLTableSource updateTableSource = selectParser.parseTableSource(); 342 update.setTableSource(updateTableSource); 343 344 accept(Token.SET); 345 346 for (;;) { 347 SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem(); 348 update.addItem(item); 349 350 if (lexer.token() != Token.COMMA) { 351 break; 352 } 353 354 lexer.nextToken(); 355 } 356 357 if (lexer.token() == (Token.WHERE)) { 358 lexer.nextToken(); 359 update.setWhere(this.exprParser.expr()); 360 } 361 362 update.setOrderBy(this.exprParser.parseOrderBy()); 363 update.setLimit(this.exprParser.parseLimit()); 364 365 return update; 366 } 367 368 protected void parseInto(SQLSelectQueryBlock queryBlock) { 369 if (lexer.token() == (Token.INTO)) { 370 lexer.nextToken(); 371 372 if (lexer.identifierEquals("OUTFILE")) { 373 lexer.nextToken(); 374 375 MySqlOutFileExpr outFile = new MySqlOutFileExpr(); 376 outFile.setFile(expr()); 377 378 queryBlock.setInto(outFile); 379 380 if (lexer.identifierEquals("FIELDS") || lexer.identifierEquals("COLUMNS")) { 381 lexer.nextToken(); 382 383 if (lexer.identifierEquals("TERMINATED")) { 384 lexer.nextToken(); 385 accept(Token.BY); 386 } 387 outFile.setColumnsTerminatedBy(expr()); 388 389 if (lexer.identifierEquals("OPTIONALLY")) { 390 lexer.nextToken(); 391 outFile.setColumnsEnclosedOptionally(true); 392 } 393 394 if (lexer.identifierEquals("ENCLOSED")) { 395 lexer.nextToken(); 396 accept(Token.BY); 397 outFile.setColumnsEnclosedBy(cast(SQLLiteralExpr) expr()); 398 } 399 400 if (lexer.identifierEquals("ESCAPED")) { 401 lexer.nextToken(); 402 accept(Token.BY); 403 outFile.setColumnsEscaped(cast(SQLLiteralExpr) expr()); 404 } 405 } 406 407 if (lexer.identifierEquals("LINES")) { 408 lexer.nextToken(); 409 410 if (lexer.identifierEquals("STARTING")) { 411 lexer.nextToken(); 412 accept(Token.BY); 413 outFile.setLinesStartingBy(cast(SQLLiteralExpr) expr()); 414 } else { 415 lexer.identifierEquals("TERMINATED"); 416 lexer.nextToken(); 417 accept(Token.BY); 418 outFile.setLinesTerminatedBy(cast(SQLLiteralExpr) expr()); 419 } 420 } 421 } else { 422 SQLExpr intoExpr = this.exprParser.name(); 423 if (lexer.token() == Token.COMMA) { 424 SQLListExpr list = new SQLListExpr(); 425 list.addItem(intoExpr); 426 427 while (lexer.token() == Token.COMMA) { 428 lexer.nextToken(); 429 SQLName name = this.exprParser.name(); 430 list.addItem(name); 431 } 432 433 intoExpr = list; 434 } 435 queryBlock.setInto(intoExpr); 436 } 437 } 438 } 439 440 override protected SQLTableSource primaryTableSourceRest(SQLTableSource tableSource) { 441 parseIndexHintList(tableSource); 442 443 if (lexer.token() == Token.PARTITION) { 444 lexer.nextToken(); 445 accept(Token.LPAREN); 446 this.exprParser.names((cast(SQLExprTableSource) tableSource).getPartitions(), tableSource); 447 accept(Token.RPAREN); 448 } 449 450 return tableSource; 451 } 452 453 override protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) { 454 if (lexer.identifierEquals(FnvHash.Constants.USING)) { 455 return tableSource; 456 } 457 458 parseIndexHintList(tableSource); 459 460 if (lexer.token() == Token.PARTITION) { 461 lexer.nextToken(); 462 accept(Token.LPAREN); 463 this.exprParser.names((cast(SQLExprTableSource) tableSource).getPartitions(), tableSource); 464 accept(Token.RPAREN); 465 } 466 467 return super.parseTableSourceRest(tableSource); 468 } 469 470 private void parseIndexHintList(SQLTableSource tableSource) { 471 if (lexer.token() == Token.USE) { 472 lexer.nextToken(); 473 MySqlUseIndexHint hint = new MySqlUseIndexHint(); 474 parseIndexHint(hint); 475 tableSource.getHints().add(hint); 476 parseIndexHintList(tableSource); 477 } 478 479 if (lexer.identifierEquals(FnvHash.Constants.IGNORE)) { 480 lexer.nextToken(); 481 MySqlIgnoreIndexHint hint = new MySqlIgnoreIndexHint(); 482 parseIndexHint(hint); 483 tableSource.getHints().add(hint); 484 parseIndexHintList(tableSource); 485 } 486 487 if (lexer.identifierEquals(FnvHash.Constants.FORCE)) { 488 lexer.nextToken(); 489 MySqlForceIndexHint hint = new MySqlForceIndexHint(); 490 parseIndexHint(hint); 491 tableSource.getHints().add(hint); 492 parseIndexHintList(tableSource); 493 } 494 } 495 496 private void parseIndexHint(MySqlIndexHintImpl hint) { 497 if (lexer.token() == Token.INDEX) { 498 lexer.nextToken(); 499 } else { 500 accept(Token.KEY); 501 } 502 503 if (lexer.token() == Token.FOR) { 504 lexer.nextToken(); 505 506 if (lexer.token() == Token.JOIN) { 507 lexer.nextToken(); 508 hint.setOption(MySqlIndexHint.Option.JOIN); 509 } else if (lexer.token() == Token.ORDER) { 510 lexer.nextToken(); 511 accept(Token.BY); 512 hint.setOption(MySqlIndexHint.Option.ORDER_BY); 513 } else { 514 accept(Token.GROUP); 515 accept(Token.BY); 516 hint.setOption(MySqlIndexHint.Option.GROUP_BY); 517 } 518 } 519 520 accept(Token.LPAREN); 521 if (lexer.token() == Token.PRIMARY) { 522 lexer.nextToken(); 523 hint.getIndexList().add(new SQLIdentifierExpr("PRIMARY")); 524 } else { 525 this.exprParser.names(hint.getIndexList()); 526 } 527 accept(Token.RPAREN); 528 } 529 530 override public SQLUnionQuery unionRest(SQLUnionQuery union_p) { 531 if (lexer.token() == Token.LIMIT) { 532 union_p.setLimit(this.exprParser.parseLimit()); 533 } 534 return super.unionRest(union_p); 535 } 536 537 public MySqlExprParser getExprParser() { 538 return cast(MySqlExprParser) exprParser; 539 } 540 }