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.ast.statement.MySqlCreateTableStatement;
17 
18 import hunt.collection;
19 
20 import hunt.sql.SQLUtils;
21 import hunt.sql.ast;
22 import hunt.sql.ast.statement;
23 import hunt.sql.dialect.mysql.ast.MySqlKey;
24 import hunt.sql.dialect.mysql.ast.MySqlObjectImpl;
25 import hunt.sql.dialect.mysql.ast.MySqlUnique;
26 import hunt.sql.dialect.mysql.visitor.MySqlASTVisitor;
27 import hunt.sql.dialect.mysql.visitor.MySqlOutputVisitor;
28 import hunt.sql.dialect.mysql.visitor.MySqlShowColumnOutpuVisitor;
29 import hunt.sql.visitor.SQLASTVisitor;
30 import hunt.sql.util.DBType;
31 import hunt.sql.dialect.mysql.ast.statement.MySqlStatement;
32 import hunt.sql.dialect.mysql.ast.statement.MySqlAlterTableOption;
33 import hunt.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement;
34 import hunt.sql.dialect.mysql.ast.statement.MySqlAlterTableAlterColumn;
35 import hunt.sql.dialect.mysql.ast.statement.MySqlAlterTableChangeColumn;
36 import hunt.sql.dialect.mysql.ast.statement.MySqlAlterTableModifyColumn;
37 import hunt.sql.dialect.mysql.ast.statement.MySqlTableIndex;
38 
39 import hunt.util.Appendable;
40 import hunt.util.Common;
41 import hunt.util.StringBuilder;
42 
43 public class MySqlCreateTableStatement : SQLCreateTableStatement , MySqlStatement {
44 
45     alias cloneTo = SQLCreateTableStatement.cloneTo;
46 
47     private Map!(string, SQLObject) tableOptions = new LinkedHashMap!(string, SQLObject)();
48     private List!(SQLCommentHint)   hints       ;
49     private List!(SQLCommentHint)   optionHints ;
50     private SQLName                tableGroup;
51 
52     protected SQLPartitionBy dbPartitionBy;
53     protected SQLPartitionBy tablePartitionBy;
54     protected SQLExpr        tbpartitions;
55 
56     public this(){
57         hints        = new ArrayList!(SQLCommentHint)();
58         optionHints  = new ArrayList!(SQLCommentHint)();
59         super (DBType.MYSQL.name);
60     }
61 
62 
63 
64     public List!(SQLCommentHint) getHints() {
65         return hints;
66     }
67 
68     public void setHints(List!(SQLCommentHint) hints) {
69         this.hints = hints;
70     }
71 
72     public void setTableOptions(Map!(string, SQLObject) tableOptions) {
73         this.tableOptions = tableOptions;
74     }
75 
76     //@Deprecated
77     public SQLSelect getQuery() {
78         return select;
79     }
80 
81     //@Deprecated
82     public void setQuery(SQLSelect query) {
83         this.select = query;
84     }
85 
86     
87     override  protected void accept0(SQLASTVisitor visitor) {
88         if (cast(MySqlASTVisitor)(visitor) !is null) {
89             accept0(cast(MySqlASTVisitor) visitor);
90         } else {
91             super.accept0(visitor);
92         }
93     }
94 
95     public void accept0(MySqlASTVisitor visitor) {
96         if (visitor.visit(this)) {
97             this.acceptChild!SQLCommentHint(visitor, getHints());
98             this.acceptChild(visitor, getTableSource());
99             this.acceptChild!SQLTableElement(visitor, getTableElementList());
100             this.acceptChild(visitor, getLike());
101             this.acceptChild(visitor, getSelect());
102         }
103         visitor.endVisit(this);
104     }
105 
106     public static class TableSpaceOption : MySqlObjectImpl {
107 
108         alias accept0 = MySqlObjectImpl.accept0;
109 
110         private SQLName name;
111         private SQLExpr storage;
112 
113         public SQLName getName() {
114             return name;
115         }
116 
117         public void setName(SQLName name) {
118             if (name !is null) {
119                 name.setParent(this);
120             }
121             this.name = name;
122         }
123 
124         public SQLExpr getStorage() {
125             return storage;
126         }
127 
128         public void setStorage(SQLExpr storage) {
129             if (storage !is null) {
130                 storage.setParent(this);
131             }
132             this.storage = storage;
133         }
134 
135         override
136         public void accept0(MySqlASTVisitor visitor) {
137             if (visitor.visit(this)) {
138                 acceptChild(visitor, getName());
139                 acceptChild(visitor, getStorage());
140             }
141             visitor.endVisit(this);
142         }
143 
144         override public TableSpaceOption clone() {
145             TableSpaceOption x = new TableSpaceOption();
146 
147             if (name !is null) {
148                 x.setName(name.clone());
149             }
150 
151             if (storage !is null) {
152                 x.setStorage(storage.clone());
153             }
154 
155             return x;
156         }
157 
158     }
159 
160     public List!(SQLCommentHint) getOptionHints() {
161         return optionHints;
162     }
163 
164     public void setOptionHints(List!(SQLCommentHint) optionHints) {
165         this.optionHints = optionHints;
166     }
167 
168     
169     public SQLName getTableGroup() {
170         return tableGroup;
171     }
172 
173     public void setTableGroup(SQLName tableGroup) {
174         this.tableGroup = tableGroup;
175     }
176 
177     override
178     public void simplify() {
179         tableOptions.clear();
180         super.simplify();
181     }
182 
183     public void showCoumns(Appendable out_p) {
184         this.accept(new MySqlShowColumnOutpuVisitor(out_p));
185     }
186 
187     public bool apply(MySqlRenameTableStatement x) {
188         foreach(MySqlRenameTableStatement.Item item ; x.getItems()) {
189             if (apply(item)) {
190                 return true;
191             }
192         }
193 
194         return false;
195     }
196 
197     override protected bool alterApply(SQLAlterTableItem item) {
198         if (cast(MySqlAlterTableAlterColumn)(item) !is null) {
199             return apply(cast(MySqlAlterTableAlterColumn) item);
200 
201         } else if (cast(MySqlAlterTableChangeColumn)(item) !is null) {
202             return apply(cast(MySqlAlterTableChangeColumn) item);
203 
204         } else if (cast(SQLAlterCharacter)(item) !is null) {
205             return apply(cast(SQLAlterCharacter) item);
206 
207         } else if (cast(MySqlAlterTableModifyColumn)(item) !is null) {
208             return apply(cast(MySqlAlterTableModifyColumn) item);
209 
210         } else if (cast(MySqlAlterTableOption)(item) !is null) {
211             return apply(cast(MySqlAlterTableOption) item);
212         }
213 
214         return super.alterApply(item);
215     }
216 
217     override public bool apply(SQLAlterTableAddIndex item) {
218         if (item.isUnique()) {
219             MySqlUnique x = new MySqlUnique();
220             item.cloneTo(x);
221             x.setParent(this);
222             this.tableElementList.add(x);
223             return true;
224         }
225 
226         if (item.isKey()) {
227             MySqlKey x = new MySqlKey();
228             item.cloneTo(x);
229             x.setParent(this);
230             this.tableElementList.add(x);
231             return true;
232         }
233 
234         MySqlTableIndex x = new MySqlTableIndex();
235         item.cloneTo(x);
236         x.setParent(this);
237         this.tableElementList.add(x);
238         return true;
239     }
240 
241     public bool apply(MySqlAlterTableOption item) {
242         this.tableOptions.put(item.getName(), item.getValue());
243         return true;
244     }
245 
246     public bool apply(SQLAlterCharacter item) {
247         SQLExpr charset = item.getCharacterSet();
248         if (charset !is null) {
249             this.tableOptions.put("CHARACTER SET", charset);
250         }
251 
252         SQLExpr collate = item.getCollate();
253         if (collate !is null) {
254             this.tableOptions.put("COLLATE", collate);
255         }
256         return true;
257     }
258 
259     public bool apply(MySqlRenameTableStatement.Item item) {
260         if (!SQLUtils.nameEquals(cast(SQLName) item.getName(), this.getName())) {
261             return false;
262         }
263         this.setName(cast(SQLName) item.getTo().clone());
264         return true;
265     }
266 
267     public bool apply(MySqlAlterTableAlterColumn x) {
268         int columnIndex = columnIndexOf(x.getColumn());
269         if (columnIndex == -1) {
270             return false;
271         }
272 
273         SQLExpr defaultExpr = x.getDefaultExpr();
274         SQLColumnDefinition column = cast(SQLColumnDefinition) tableElementList.get(columnIndex);
275 
276         if (x.isDropDefault()) {
277             column.setDefaultExpr(null);
278         } else if (defaultExpr !is null) {
279             column.setDefaultExpr(defaultExpr);
280         }
281 
282         return true;
283     }
284 
285     public bool apply(MySqlAlterTableChangeColumn item) {
286         SQLName columnName = item.getColumnName();
287         int columnIndex = columnIndexOf(columnName);
288         if (columnIndex == -1) {
289             return false;
290         }
291 
292         int afterIndex = columnIndexOf(item.getAfterColumn());
293         int beforeIndex = columnIndexOf(item.getFirstColumn());
294 
295         int insertIndex = -1;
296         if (beforeIndex != -1) {
297             insertIndex = beforeIndex;
298         } else if (afterIndex != -1) {
299             insertIndex = afterIndex + 1;
300         } else if (item.isFirst()) {
301             insertIndex = 0;
302         }
303 
304         SQLColumnDefinition column = item.getNewColumnDefinition().clone();
305         column.setParent(this);
306         if (insertIndex == -1 || insertIndex == columnIndex) {
307             tableElementList.set(columnIndex, column);
308         } else {
309             if (insertIndex > columnIndex) {
310                 tableElementList.add(insertIndex, column);
311                 tableElementList.removeAt(columnIndex);
312             } else {
313                 tableElementList.removeAt(columnIndex);
314                 tableElementList.add(insertIndex, column);
315             }
316         }
317 
318         for (int i = 0; i < tableElementList.size(); i++) {
319             SQLTableElement e = tableElementList.get(i);
320             if(cast(MySqlTableIndex)(e) !is null) {
321                 (cast(MySqlTableIndex) e).applyColumnRename(columnName, column.getName());
322             } else if (cast(SQLUnique)(e) !is null) {
323                 SQLUnique unique = cast(SQLUnique) e;
324                 unique.applyColumnRename(columnName, column.getName());
325             }
326         }
327 
328         return true;
329     }
330 
331     public bool apply(MySqlAlterTableModifyColumn item) {
332         SQLColumnDefinition column = item.getNewColumnDefinition().clone();
333         SQLName columnName = column.getName();
334 
335         int columnIndex = columnIndexOf(columnName);
336         if (columnIndex == -1) {
337             return false;
338         }
339 
340         int afterIndex = columnIndexOf(item.getAfterColumn());
341         int beforeIndex = columnIndexOf(item.getFirstColumn());
342 
343         int insertIndex = -1;
344         if (beforeIndex != -1) {
345             insertIndex = beforeIndex;
346         } else if (afterIndex != -1) {
347             insertIndex = afterIndex + 1;
348         }
349 
350         column.setParent(this);
351         if (insertIndex == -1 || insertIndex == columnIndex) {
352             tableElementList.set(columnIndex, column);
353             return true;
354         } else {
355             if (insertIndex > columnIndex) {
356                 tableElementList.add(insertIndex, column);
357                 tableElementList.removeAt(columnIndex);
358             } else {
359                 tableElementList.removeAt(columnIndex);
360                 tableElementList.add(insertIndex, column);
361             }
362         }
363 
364         return true;
365     }
366 
367     override public void output(StringBuilder buf) {
368         this.accept(new MySqlOutputVisitor(buf));
369     }
370 
371     public void cloneTo(MySqlCreateTableStatement x) {
372         super.cloneTo(x);
373         foreach(string k, SQLObject v ; tableOptions) {
374             SQLObject obj = v.clone();
375             obj.setParent(x);
376             x.tableOptions.put(k, obj);
377         }
378         if (partitioning !is null) {
379             x.setPartitioning(partitioning.clone());
380         }
381         foreach(SQLCommentHint hint ; hints) {
382             SQLCommentHint h2 = hint.clone();
383             h2.setParent(x);
384             x.hints.add(h2);
385         }
386         foreach(SQLCommentHint hint ; optionHints) {
387             SQLCommentHint h2 = hint.clone();
388             h2.setParent(x);
389             x.optionHints.add(h2);
390         }
391         if (like !is null) {
392             x.setLike(like.clone());
393         }
394         if (tableGroup !is null) {
395             x.setTableGroup(tableGroup.clone());
396         }
397     }
398 
399     override public MySqlCreateTableStatement clone() {
400         MySqlCreateTableStatement x = new MySqlCreateTableStatement();
401         cloneTo(x);
402         return x;
403     }
404 
405     public SQLPartitionBy getDbPartitionBy() {
406         return dbPartitionBy;
407     }
408 
409     public void setDbPartitionBy(SQLPartitionBy x) {
410         if (x !is null) {
411             x.setParent(this);
412         }
413         this.dbPartitionBy = x;
414     }
415 
416     public SQLPartitionBy getTablePartitionBy() {
417         return tablePartitionBy;
418     }
419 
420     public void setTablePartitionBy(SQLPartitionBy x) {
421         if (x !is null) {
422             x.setParent(this);
423         }
424         this.tablePartitionBy = x;
425     }
426 
427     public SQLExpr getTbpartitions() {
428         return tbpartitions;
429     }
430 
431     public void setTbpartitions(SQLExpr x) {
432         if (x !is null) {
433             x.setParent(this);
434         }
435         this.tbpartitions = x;
436     }
437 }