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.parser.SQLCreateTableParser;
17 
18 import hunt.collection;
19 
20 import hunt.sql.ast.SQLExpr;
21 import hunt.sql.ast.SQLName;
22 import hunt.sql.ast.statement;
23 import hunt.sql.util.FnvHash;
24 import hunt.sql.util.DBType;
25 import hunt.sql.ast.statement.SQLConstraint;
26 import hunt.sql.ast.statement.SQLTableElement;
27 import hunt.sql.parser.Token;
28 import hunt.sql.parser.ParserException;
29 import hunt.String;
30 import hunt.sql.parser.SQLDDLParser;
31 import hunt.sql.parser.SQLExprParser;
32 import hunt.text;
33 
34 public class SQLCreateTableParser : SQLDDLParser {
35 
36     public this(string sql) {
37         super(sql);
38     }
39 
40     public this(SQLExprParser exprParser) {
41         super(exprParser);
42     }
43 
44     override public SQLCreateTableStatement parseCreateTable() {
45         List!(string) comments = null;
46         if (lexer.isKeepComments() && lexer.hasComment()) {
47             comments = lexer.readAndResetComments();
48         }
49 
50         SQLCreateTableStatement stmt = parseCreateTable(true);
51         if (comments !is null) {
52             stmt.addBeforeComment(comments);
53         }
54 
55         return stmt;
56     }
57 
58     public SQLCreateTableStatement parseCreateTable(bool acceptCreate) {
59         SQLCreateTableStatement createTable = newCreateStatement();
60 
61         if (acceptCreate) {
62             if (lexer.hasComment() && lexer.isKeepComments()) {
63                 createTable.addBeforeComment(lexer.readAndResetComments());
64             }
65 
66             accept(Token.CREATE);
67         }
68 
69         if (lexer.identifierEquals("GLOBAL")) {
70             lexer.nextToken();
71 
72             if (lexer.identifierEquals("TEMPORARY")) {
73                 lexer.nextToken();
74                 createTable.setType(SQLCreateTableStatement.Type.GLOBAL_TEMPORARY);
75             } else {
76                 throw new ParserException("syntax error " ~ lexer.info());
77             }
78         } else if (lexer.token == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase("LOCAL")) {
79             lexer.nextToken();
80             if (lexer.token == Token.IDENTIFIER && lexer.stringVal().equalsIgnoreCase("TEMPORAY")) {
81                 lexer.nextToken();
82                 createTable.setType(SQLCreateTableStatement.Type.LOCAL_TEMPORARY);
83             } else {
84                 throw new ParserException("syntax error. " ~ lexer.info());
85             }
86         }
87 
88         accept(Token.TABLE);
89 
90         if (lexer.token() == Token.IF) {
91             lexer.nextToken();
92             accept(Token.NOT);
93             accept(Token.EXISTS);
94 
95             createTable.setIfNotExiists(true);
96         }
97 
98         createTable.setName(this.exprParser.name());
99 
100         if (lexer.token == Token.LPAREN) {
101             lexer.nextToken();
102 
103             for (; ; ) {
104                 Token token = lexer.token;
105                 if (token == Token.IDENTIFIER
106                         && lexer.stringVal().equalsIgnoreCase("SUPPLEMENTAL")
107                         && DBType.ORACLE.opEquals(dbType)) {
108                     this.parseCreateTableSupplementalLogingProps(createTable);
109                 } else if (token == Token.IDENTIFIER //
110                         || token == Token.LITERAL_ALIAS) {
111                     SQLColumnDefinition column = this.exprParser.parseColumn();
112                     createTable.getTableElementList().add(column);
113                 } else if (token == Token.PRIMARY //
114                         || token == Token.UNIQUE //
115                         || token == Token.CHECK //
116                         || token == Token.CONSTRAINT
117                         || token == Token.FOREIGN) {
118                     SQLConstraint constraint = this.exprParser.parseConstaint();
119                     constraint.setParent(createTable);
120                     createTable.getTableElementList().add(cast(SQLTableElement) constraint);
121                 } else if (token == Token.TABLESPACE) {
122                     throw new ParserException("TODO "  ~ lexer.info());
123                 } else {
124                     SQLColumnDefinition column = this.exprParser.parseColumn();
125                     createTable.getTableElementList().add(column);
126                 }
127 
128                 if (lexer.token == Token.COMMA) {
129                     lexer.nextToken();
130 
131                     if (lexer.token == Token.RPAREN) { // compatible for sql server
132                         break;
133                     }
134                     continue;
135                 }
136 
137                 break;
138             }
139 
140             accept(Token.RPAREN);
141 
142             if (lexer.identifierEquals(FnvHash.Constants.INHERITS)) {
143                 lexer.nextToken();
144                 accept(Token.LPAREN);
145                 SQLName inherits = this.exprParser.name();
146                 createTable.setInherits(new SQLExprTableSource(inherits));
147                 accept(Token.RPAREN);
148             }
149         }
150 
151         if (lexer.token == Token.AS) {
152             lexer.nextToken();
153             SQLSelect select = this.createSQLSelectParser().select();
154             createTable.setSelect(select);
155         }
156 
157         if (lexer.token == Token.WITH && DBType.POSTGRESQL.opEquals(dbType)) {
158             lexer.nextToken();
159             accept(Token.LPAREN);
160 
161             for (;;) {
162                 string name = lexer.stringVal();
163                 lexer.nextToken();
164                 accept(Token.EQ);
165                 SQLExpr value = this.exprParser.expr();
166                 value.setParent(createTable);
167 
168                 createTable.getTableOptions().put(name, value);
169 
170                 if (lexer.token == Token.COMMA) {
171                     lexer.nextToken();
172                     continue;
173                 }
174 
175                 break;
176             }
177             accept(Token.RPAREN);
178         }
179 
180         return createTable;
181     }
182 
183     protected void parseCreateTableSupplementalLogingProps(SQLCreateTableStatement stmt) {
184         throw new ParserException("TODO " ~ lexer.info());
185     }
186 
187     override protected SQLCreateTableStatement newCreateStatement() {
188         return new SQLCreateTableStatement(getDbType());
189     }
190 }