diff --git a/src/main/java/com/alibaba/druid/sql/dialect/h2/visitor/H2OutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/h2/visitor/H2OutputVisitor.java index 9bcb621d40..bcb7415620 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/h2/visitor/H2OutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/h2/visitor/H2OutputVisitor.java @@ -16,17 +16,23 @@ package com.alibaba.druid.sql.dialect.h2.visitor; import com.alibaba.druid.DbType; -import com.alibaba.druid.sql.ast.SQLCommentHint; import com.alibaba.druid.sql.ast.SQLExpr; -import com.alibaba.druid.sql.ast.SQLName; +import com.alibaba.druid.sql.ast.expr.SQLBinaryExpr; +import com.alibaba.druid.sql.ast.expr.SQLHexExpr; +import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLQueryExpr; import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableModifyColumn; import com.alibaba.druid.sql.visitor.SQLASTOutputVisitor; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; import java.util.List; -import java.util.Map; public class H2OutputVisitor extends SQLASTOutputVisitor implements H2ASTVisitor { + + private final static Log LOG = LogFactory.getLog(H2OutputVisitor.class); + public H2OutputVisitor(Appendable appender) { super(appender, DbType.h2); } @@ -104,6 +110,90 @@ public class H2OutputVisitor extends SQLASTOutputVisitor implements H2ASTVisitor return false; } + @Override + public boolean visit(SQLCreateTableStatement x) { + + /* + https://h2database.com/html/commands.html#create_table + CREATE [ CACHED | MEMORY ] [ { TEMP } | [ GLOBAL | LOCAL ] TEMPORARY ] + TABLE [ IF NOT EXISTS ] [schemaName.]tableName + [ ( { columnName [columnDefinition] | tableConstraintDefinition } [,...] ) ] + [ ENGINE tableEngineName ] + [ WITH tableEngineParamName [,...] ] + [ NOT PERSISTENT ] [ TRANSACTIONAL ] + [ AS query [ WITH [ NO ] DATA ] ] + */ + + printCreateTable(x, true); + return false; + } + + protected void printTableElements(List tableElementList) { + int size = tableElementList.size(); + if (size == 0) { + return; + } + + print0(" ("); + + this.indentCount++; + println(); + for (int i = 0; i < size; ++i) { + SQLTableElement element = tableElementList.get(i); + if (element instanceof SQLPrimaryKey) { + printUcase("PRIMARY KEY "); + acceptChildName(((SQLPrimaryKey) element).getColumns()); + } else if (element instanceof SQLUnique) { + if ("UNIQUE".equalsIgnoreCase(((SQLUnique) element).getIndexDefinition().getType())) { + printUcase("UNIQUE "); + } + printUcase("KEY "); + print(((SQLUnique) element).getName().getSimpleName().replaceFirst("`$", System.nanoTime() + "`")); + acceptChildName(((SQLUnique) element).getColumns()); + } else { + element.accept(this); + } + + if (i != size - 1) { + print(','); + } + if (this.isPrettyFormat() && element.hasAfterComment()) { + print(' '); + printlnComment(element.getAfterCommentsDirect()); + } + + if (i != size - 1) { + println(); + } + } + this.indentCount--; + println(); + print(')'); + } + + private void acceptChildName(List children) { + if (children == null) { + return; + } + + print('('); + for (int i = 0; i < children.size(); i++) { + SQLSelectOrderByItem child = children.get(i); + if (child == null) { + continue; + } + if (child.getExpr() instanceof SQLMethodInvokeExpr) { + print0(((SQLMethodInvokeExpr) child.getExpr()).getMethodName()); + } else { + print0(child.getExpr().toString()); + } + if (i != children.size() - 1) { + print(','); + } + } + print(')'); + } + @Override public boolean visit(SQLCreateIndexStatement x) { @@ -138,4 +228,104 @@ public class H2OutputVisitor extends SQLASTOutputVisitor implements H2ASTVisitor return false; } + @Override + public boolean visit(SQLSetStatement x) { + return false; + } + + @Override + public boolean visit(SQLHexExpr x) { + print0("X'"); + print0(x.getHex()); + print0("'"); + return false; + } + + @Override + public boolean visit(SQLBinaryExpr x) { + print0(x.getText()); + return false; + } + + @Override + protected void printChars(String text) { + if ("0000-00-00 00:00:00".equals(text)) { + // TODO: this is not correct because '0000-00-00 00:00:00' could be a real string value + LOG.warn("Replacing '0000-00-00 00:00:00' with valid H2 datetime (unsafe replacement)"); + text = "0001-01-01 00:00:00"; + } + super.printChars(text); + } + + @Override + public boolean visit(SQLAlterTableStatement x) { + + /* + https://h2database.com/html/commands.html#alter_table_alter_column + ALTER TABLE [ IF EXISTS ] [schemaName.]tableName + ALTER COLUMN [ IF EXISTS ] columnName + { { columnDefinition } + | { RENAME TO name } + | SET GENERATED { ALWAYS | BY DEFAULT } [ alterIdentityColumnOption [...] ] + | alterIdentityColumnOption [...] + | DROP IDENTITY + | { SELECTIVITY int } + | { SET DEFAULT expression } + | { DROP DEFAULT } + | DROP EXPRESSION + | { SET ON UPDATE expression } + | { DROP ON UPDATE } + | { SET DEFAULT ON NULL } + | { DROP DEFAULT ON NULL } + | { SET NOT NULL } + | { DROP NOT NULL } | { SET NULL } + | { SET DATA TYPE dataTypeOrDomain [ USING newValueExpression ] } + | { SET { VISIBLE | INVISIBLE } } } + */ + + for (int i = 0; i < x.getItems().size(); ++i) { + print0(ucase ? "ALTER TABLE " : "alter table "); + + if (x.isIfExists()) { + print0(ucase ? "IF EXISTS " : "if exists "); + } + + printTableSourceExpr(x.getName()); + + SQLAlterTableItem item = x.getItems().get(i); + accept(item); + if (i != x.getItems().size() - 1) { + print(';'); + } + } + + return false; + } + + private void accept(SQLAlterTableItem item) { + if (item instanceof MySqlAlterTableModifyColumn) { + MySqlAlterTableModifyColumn x = (MySqlAlterTableModifyColumn) item; + printUcase("ALTER COLUMN "); + x.getNewColumnDefinition().accept(this); + } else { + item.accept(this); + } + } + + public boolean visit(SQLAlterTableAddConstraint x) { + if (x.isWithNoCheck()) { + this.print0(this.ucase ? "WITH NOCHECK " : "with nocheck "); + } + + this.print0(this.ucase ? "ADD " : "add "); + + if (x.getConstraint().getParent() instanceof SQLAlterTableAddConstraint && x.getConstraint() instanceof SQLForeignKeyImpl) { + this.visit((SQLForeignKeyImpl) x.getConstraint()); + } else { + x.getConstraint().accept(this); + } + + return false; + } + }