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.MySqlSelectIntoParser; 17 18 19 import hunt.collection; 20 21 import hunt.sql.ast.SQLExpr; 22 import hunt.sql.ast.SQLSetQuantifier; 23 import hunt.sql.ast.expr.SQLIdentifierExpr; 24 import hunt.sql.ast.expr.SQLLiteralExpr; 25 import hunt.sql.ast.expr.SQLVariantRefExpr; 26 import hunt.sql.ast.statement.SQLSelect; 27 import hunt.sql.ast.statement.SQLSelectQuery; 28 import hunt.sql.ast.statement.SQLSelectQueryBlock; 29 import hunt.sql.ast.statement.SQLTableSource; 30 import hunt.sql.ast.statement.SQLUnionQuery; 31 import hunt.sql.dialect.mysql.ast.MySqlForceIndexHint; 32 import hunt.sql.dialect.mysql.ast.MySqlIgnoreIndexHint; 33 import hunt.sql.dialect.mysql.ast.MySqlIndexHint; 34 import hunt.sql.dialect.mysql.ast.MySqlIndexHintImpl; 35 import hunt.sql.dialect.mysql.ast.MySqlUseIndexHint; 36 import hunt.sql.dialect.mysql.ast.clause.MySqlSelectIntoStatement; 37 import hunt.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; 38 import hunt.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock; 39 import hunt.sql.parser.ParserException; 40 import hunt.sql.parser.SQLExprParser; 41 import hunt.sql.parser.SQLSelectParser; 42 import hunt.sql.parser.Token; 43 import hunt.sql.dialect.mysql.parser.MySqlExprParser; 44 import hunt.sql.ast.SQLObject; 45 import hunt.Boolean; 46 import hunt.sql.ast.SQLCommentHint; 47 48 public class MySqlSelectIntoParser : SQLSelectParser { 49 private List!(SQLExpr) argsList; 50 51 public this(SQLExprParser exprParser){ 52 super(exprParser); 53 } 54 55 public this(string sql){ 56 this(new MySqlExprParser(sql)); 57 } 58 59 public MySqlSelectIntoStatement parseSelectInto() 60 { 61 SQLSelect select=select(); 62 MySqlSelectIntoStatement stmt=new MySqlSelectIntoStatement(); 63 stmt.setSelect(select); 64 stmt.setVarList(argsList); 65 return stmt; 66 67 } 68 69 override 70 public SQLSelectQuery query() { 71 if (lexer.token() == (Token.LPAREN)) { 72 lexer.nextToken(); 73 74 SQLSelectQuery select = query(); 75 accept(Token.RPAREN); 76 77 return queryRest(select); 78 } 79 80 MySqlSelectQueryBlock queryBlock = new MySqlSelectQueryBlock(); 81 82 if (lexer.token() == Token.SELECT) { 83 lexer.nextToken(); 84 85 if (lexer.token() == Token.HINT) { 86 this.exprParser.parseHints!(SQLCommentHint)((queryBlock.getHints())); 87 } 88 89 if (lexer.token() == Token.COMMENT) { 90 lexer.nextToken(); 91 } 92 93 if (lexer.token() == (Token.DISTINCT)) { 94 queryBlock.setDistionOption(SQLSetQuantifier.DISTINCT); 95 lexer.nextToken(); 96 } else if (lexer.identifierEquals("DISTINCTROW")) { 97 queryBlock.setDistionOption(SQLSetQuantifier.DISTINCTROW); 98 lexer.nextToken(); 99 } else if (lexer.token() == (Token.ALL)) { 100 queryBlock.setDistionOption(SQLSetQuantifier.ALL); 101 lexer.nextToken(); 102 } 103 104 if (lexer.identifierEquals("HIGH_PRIORITY")) { 105 queryBlock.setHignPriority(true); 106 lexer.nextToken(); 107 } 108 109 if (lexer.identifierEquals("STRAIGHT_JOIN")) { 110 queryBlock.setStraightJoin(true); 111 lexer.nextToken(); 112 } 113 114 if (lexer.identifierEquals("SQL_SMALL_RESULT")) { 115 queryBlock.setSmallResult(true); 116 lexer.nextToken(); 117 } 118 119 if (lexer.identifierEquals("SQL_BIG_RESULT")) { 120 queryBlock.setBigResult(true); 121 lexer.nextToken(); 122 } 123 124 if (lexer.identifierEquals("SQL_BUFFER_RESULT")) { 125 queryBlock.setBufferResult(true); 126 lexer.nextToken(); 127 } 128 129 if (lexer.identifierEquals("SQL_CACHE")) { 130 queryBlock.setCache(new Boolean(true)); 131 lexer.nextToken(); 132 } 133 134 if (lexer.identifierEquals("SQL_NO_CACHE")) { 135 queryBlock.setCache(new Boolean(false)); 136 lexer.nextToken(); 137 } 138 139 if (lexer.identifierEquals("SQL_CALC_FOUND_ROWS")) { 140 queryBlock.setCalcFoundRows(true); 141 lexer.nextToken(); 142 } 143 144 parseSelectList(queryBlock); 145 146 argsList=parseIntoArgs(); 147 } 148 149 parseFrom(queryBlock); 150 151 parseWhere(queryBlock); 152 153 parseGroupBy(queryBlock); 154 155 queryBlock.setOrderBy(this.exprParser.parseOrderBy()); 156 157 if (lexer.token() == Token.LIMIT) { 158 queryBlock.setLimit(this.exprParser.parseLimit()); 159 } 160 161 if (lexer.token() == Token.PROCEDURE) { 162 lexer.nextToken(); 163 throw new ParserException("TODO. " ~ lexer.info()); 164 } 165 166 parseInto(queryBlock); 167 168 if (lexer.token() == Token.FOR) { 169 lexer.nextToken(); 170 accept(Token.UPDATE); 171 172 queryBlock.setForUpdate(true); 173 } 174 175 if (lexer.token() == Token.LOCK) { 176 lexer.nextToken(); 177 accept(Token.IN); 178 acceptIdentifier("SHARE"); 179 acceptIdentifier("MODE"); 180 queryBlock.setLockInShareMode(true); 181 } 182 183 return queryRest(queryBlock); 184 } 185 /** 186 * parser the select into arguments 187 * @return 188 */ 189 protected List!(SQLExpr) parseIntoArgs() { 190 191 List!(SQLExpr) args=new ArrayList!(SQLExpr)(); 192 if (lexer.token() == (Token.INTO)) { 193 accept(Token.INTO); 194 //lexer.nextToken(); 195 for (;;) { 196 SQLExpr var = exprParser.primary(); 197 if (cast(SQLIdentifierExpr)(var) !is null) { 198 var = new SQLVariantRefExpr( 199 (cast(SQLIdentifierExpr) var).getName()); 200 } 201 args.add(var); 202 if (lexer.token() == Token.COMMA) { 203 accept(Token.COMMA); 204 continue; 205 } 206 else 207 { 208 break; 209 } 210 } 211 } 212 return args; 213 } 214 215 216 protected void parseInto(SQLSelectQueryBlock queryBlock) { 217 if (lexer.token() == (Token.INTO)) { 218 lexer.nextToken(); 219 220 if (lexer.identifierEquals("OUTFILE")) { 221 lexer.nextToken(); 222 223 MySqlOutFileExpr outFile = new MySqlOutFileExpr(); 224 outFile.setFile(expr()); 225 226 queryBlock.setInto(outFile); 227 228 if (lexer.identifierEquals("FIELDS") || lexer.identifierEquals("COLUMNS")) { 229 lexer.nextToken(); 230 231 if (lexer.identifierEquals("TERMINATED")) { 232 lexer.nextToken(); 233 accept(Token.BY); 234 } 235 outFile.setColumnsTerminatedBy(cast(SQLLiteralExpr) expr()); 236 237 if (lexer.identifierEquals("OPTIONALLY")) { 238 lexer.nextToken(); 239 outFile.setColumnsEnclosedOptionally(true); 240 } 241 242 if (lexer.identifierEquals("ENCLOSED")) { 243 lexer.nextToken(); 244 accept(Token.BY); 245 outFile.setColumnsEnclosedBy(cast(SQLLiteralExpr) expr()); 246 } 247 248 if (lexer.identifierEquals("ESCAPED")) { 249 lexer.nextToken(); 250 accept(Token.BY); 251 outFile.setColumnsEscaped(cast(SQLLiteralExpr) expr()); 252 } 253 } 254 255 if (lexer.identifierEquals("LINES")) { 256 lexer.nextToken(); 257 258 if (lexer.identifierEquals("STARTING")) { 259 lexer.nextToken(); 260 accept(Token.BY); 261 outFile.setLinesStartingBy(cast(SQLLiteralExpr) expr()); 262 } else { 263 lexer.identifierEquals("TERMINATED"); 264 lexer.nextToken(); 265 accept(Token.BY); 266 outFile.setLinesTerminatedBy(cast(SQLLiteralExpr) expr()); 267 } 268 } 269 } else { 270 queryBlock.setInto(this.exprParser.name()); 271 } 272 } 273 } 274 275 override protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) { 276 if (lexer.identifierEquals("USING")) { 277 return tableSource; 278 } 279 280 parseIndexHintList(tableSource); 281 282 return super.parseTableSourceRest(tableSource); 283 } 284 285 private void parseIndexHintList(SQLTableSource tableSource) { 286 if (lexer.token() == Token.USE) { 287 lexer.nextToken(); 288 MySqlUseIndexHint hint = new MySqlUseIndexHint(); 289 parseIndexHint(hint); 290 tableSource.getHints().add(hint); 291 parseIndexHintList(tableSource); 292 } 293 294 if (lexer.identifierEquals("IGNORE")) { 295 lexer.nextToken(); 296 MySqlIgnoreIndexHint hint = new MySqlIgnoreIndexHint(); 297 parseIndexHint(hint); 298 tableSource.getHints().add(hint); 299 parseIndexHintList(tableSource); 300 } 301 302 if (lexer.identifierEquals("FORCE")) { 303 lexer.nextToken(); 304 MySqlForceIndexHint hint = new MySqlForceIndexHint(); 305 parseIndexHint(hint); 306 tableSource.getHints().add(hint); 307 parseIndexHintList(tableSource); 308 } 309 } 310 311 private void parseIndexHint(MySqlIndexHintImpl hint) { 312 if (lexer.token() == Token.INDEX) { 313 lexer.nextToken(); 314 } else { 315 accept(Token.KEY); 316 } 317 318 if (lexer.token() == Token.FOR) { 319 lexer.nextToken(); 320 321 if (lexer.token() == Token.JOIN) { 322 lexer.nextToken(); 323 hint.setOption(MySqlIndexHint.Option.JOIN); 324 } else if (lexer.token() == Token.ORDER) { 325 lexer.nextToken(); 326 accept(Token.BY); 327 hint.setOption(MySqlIndexHint.Option.ORDER_BY); 328 } else { 329 accept(Token.GROUP); 330 accept(Token.BY); 331 hint.setOption(MySqlIndexHint.Option.GROUP_BY); 332 } 333 } 334 335 accept(Token.LPAREN); 336 if (lexer.token() == Token.PRIMARY) { 337 lexer.nextToken(); 338 hint.getIndexList().add(new SQLIdentifierExpr("PRIMARY")); 339 } else { 340 this.exprParser.names(hint.getIndexList()); 341 } 342 accept(Token.RPAREN); 343 } 344 345 override public SQLUnionQuery unionRest(SQLUnionQuery union_p) { 346 if (lexer.token() == Token.LIMIT) { 347 union_p.setLimit(this.exprParser.parseLimit()); 348 } 349 return super.unionRest(union_p); 350 } 351 352 public MySqlExprParser getExprParser() { 353 return cast(MySqlExprParser) exprParser; 354 } 355 }