diff --git a/README.md b/README.md index 6ccccbb..4d357dc 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,37 @@ sql转换工具,sql翻译,sql格式化,支持oracle转mysql,后续支持 #### 2. 支持在表名前动态配置用户名 > 由于在项目中的表名一般不会使用[user].tableName, 但是在某些不跨库场景下,需要在A用户下,去访问B用户的表时。就需要根据表名来动态加上用户B。 +#### 1.支持oracle create语法到Starrocks的主键表的语法 + +```sql +CREATE TABLE my_table ( + ID NUMBER(10), + clob_column CLOB, + blob_column BLOB, + some_other_column VARCHAR2(100), + creation_date DATE DEFAULT SYSDATE, + last_modified DATE, + CONSTRAINT pk_my_table PRIMARY KEY (ID), + CONSTRAINT fk_department FOREIGN KEY (department_id) REFERENCES departments(department_id), + CONSTRAINT uk_my_table UNIQUE (some_other_column) + ); +``` +SR +```sql + +CREATE TABLE my_table ( + ID bigint, + clob_column VARBINARY, + blob_column VARBINARY, + some_other_column varchar(100), + creation_date datetime DEFAULT CURRENT_TIMESTAMP, + last_modified datetime +) +PRIMARY KEY (ID,some_other_column) +DISTRIBUTED BY HASH (ID,some_other_column); + +``` + + + diff --git a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/OracleToMySqlOutputVisitor.java b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/OracleToMySqlOutputVisitor.java index bb65976..53d0286 100644 --- a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/OracleToMySqlOutputVisitor.java +++ b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/OracleToMySqlOutputVisitor.java @@ -292,8 +292,10 @@ public boolean visit(OracleCreateTableStatement x) { if(MySqlUtil.containsKeyWords(columnName)){ sqlColumnDefinition.setName("`"+columnName+"`"); } + // 类型转换在这边完成 sqlColumnDefinition.setDataType(OracleSQLDataTypeTransformUtil.transformOracleToMySql(SQLParserUtils.createExprParser(sqlColumnDefinition.getDataType().toString(), DbType.oracle).parseDataType())); if(sqlColumnDefinition.getDefaultExpr() != null) { + // 获取建表语句中的default关键字 SQLExpr expr = sqlColumnDefinition.getDefaultExpr(); if(expr instanceof SQLMethodInvokeExpr) { SQLMethodInvokeExpr sqlMethodInvokeExpr = (SQLMethodInvokeExpr) expr; @@ -327,6 +329,7 @@ public boolean visit(OracleCreateTableStatement x) { } } } + //转为目标数据库的类型 sqlColumnDefinition.setDbType(distDbType); MapCacheUtil.getInstance().addCacheData(tableName.toUpperCase() + ":" + columnName.toUpperCase(), sqlColumnDefinition.toString().replaceAll(sqlColumnDefinition.getColumnName(), "")); mySqlCreateTableStatement.getTableElementList().add(sqlColumnDefinition); diff --git a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/util/OracleSQLDataTypeTransformUtil.java b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/util/OracleSQLDataTypeTransformUtil.java index 2598245..c5a01b2 100644 --- a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/util/OracleSQLDataTypeTransformUtil.java +++ b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/oracle/util/OracleSQLDataTypeTransformUtil.java @@ -26,12 +26,12 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { if (name == null) { return x; } - List argumentns = x.getArguments(); + List arguments = x.getArguments(); SQLDataType dataType; if (nameHash == FnvHash.Constants.UROWID) { int len = 4000; - if (argumentns.size() == 1) { - SQLExpr arg0 = argumentns.get(0); + if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); if (arg0 instanceof SQLIntegerExpr) { len = ((SQLIntegerExpr) arg0).getNumber().intValue(); } @@ -56,10 +56,10 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { dataType = new SQLDataTypeImpl("double"); } else if (nameHash == FnvHash.Constants.NUMBER) { - if (argumentns.size() == 0) { + if (arguments.size() == 0) { dataType = new SQLDataTypeImpl("decimal", 38); } else { - SQLExpr arg0 = argumentns.get(0); + SQLExpr arg0 = arguments.get(0); int precision, scale = 0; if (arg0 instanceof SQLAllColumnExpr) { @@ -68,8 +68,8 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { precision = ((SQLIntegerExpr) arg0).getNumber().intValue(); } - if (argumentns.size() > 1) { - scale = ((SQLIntegerExpr) argumentns.get(1)).getNumber().intValue(); + if (arguments.size() > 1) { + scale = ((SQLIntegerExpr) arguments.get(1)).getNumber().intValue(); } if (scale > precision) { @@ -107,13 +107,13 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { dataType.setName("decimal"); int precision = 0; - if (argumentns.size() > 0) { - precision = ((SQLIntegerExpr) argumentns.get(0)).getNumber().intValue(); + if (arguments.size() > 0) { + precision = ((SQLIntegerExpr) arguments.get(0)).getNumber().intValue(); } int scale = 0; - if (argumentns.size() > 1) { - scale = ((SQLIntegerExpr) argumentns.get(1)).getNumber().intValue(); + if (arguments.size() > 1) { + scale = ((SQLIntegerExpr) arguments.get(1)).getNumber().intValue(); if (precision < scale) { ((SQLIntegerExpr) dataType.getArguments().get(1)).setNumber(precision); } @@ -121,10 +121,10 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { } else if (nameHash == FnvHash.Constants.RAW) { int len; - if (argumentns.size() == 0) { + if (arguments.size() == 0) { len = -1; - } else if (argumentns.size() == 1) { - SQLExpr arg0 = argumentns.get(0); + } else if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); if (arg0 instanceof SQLNumericLiteralExpr) { len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); } else { @@ -143,8 +143,8 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { } } else if (nameHash == FnvHash.Constants.CHAR || nameHash == FnvHash.Constants.CHARACTER) { - if (argumentns.size() == 1) { - SQLExpr arg0 = argumentns.get(0); + if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); int len; if (arg0 instanceof SQLNumericLiteralExpr) { @@ -158,15 +158,15 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { } else { dataType = new SQLCharacterDataType("varchar", len); } - } else if (argumentns.size() == 0) { + } else if (arguments.size() == 0) { dataType = new SQLCharacterDataType("char"); } else { throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); } } else if (nameHash == FnvHash.Constants.NCHAR) { - if (argumentns.size() == 1) { - SQLExpr arg0 = argumentns.get(0); + if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); int len; if (arg0 instanceof SQLNumericLiteralExpr) { @@ -180,16 +180,16 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { } else { dataType = new SQLCharacterDataType("nvarchar", len); } - } else if (argumentns.size() == 0) { + } else if (arguments.size() == 0) { dataType = new SQLCharacterDataType("nchar"); } else { throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); } } else if (nameHash == FnvHash.Constants.VARCHAR2) { - if (argumentns.size() > 0) { + if (arguments.size() > 0) { int len; - SQLExpr arg0 = argumentns.get(0); + SQLExpr arg0 = arguments.get(0); if (arg0 instanceof SQLNumericLiteralExpr) { len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); } else { @@ -205,9 +205,9 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { } } else if (nameHash == FnvHash.Constants.NVARCHAR2) { - if (argumentns.size() > 0) { + if (arguments.size() > 0) { int len; - SQLExpr arg0 = argumentns.get(0); + SQLExpr arg0 = arguments.get(0); if (arg0 instanceof SQLNumericLiteralExpr) { len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); } else { @@ -224,8 +224,8 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { } else if (nameHash == FnvHash.Constants.DATE || nameHash == FnvHash.Constants.TIMESTAMP) { int len = -1; - if (argumentns.size() > 0) { - SQLExpr arg0 = argumentns.get(0); + if (arguments.size() > 0) { + SQLExpr arg0 = arguments.get(0); if (arg0 instanceof SQLNumericLiteralExpr) { len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); } else { @@ -243,14 +243,14 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { /* }*/ } else if (nameHash == FnvHash.Constants.BLOB || nameHash == FnvHash.Constants.LONG_RAW) { - argumentns.clear(); + arguments.clear(); dataType = new SQLDataTypeImpl("LONGBLOB"); } else if (nameHash == FnvHash.Constants.CLOB || nameHash == FnvHash.Constants.NCLOB || nameHash == FnvHash.Constants.LONG || nameHash == FnvHash.Constants.XMLTYPE) { - argumentns.clear(); + arguments.clear(); dataType = new SQLCharacterDataType("LONGTEXT"); } else { dataType = x; @@ -262,4 +262,253 @@ public static SQLDataType transformOracleToMySql(SQLDataType x) { return dataType; } + + /** + * oracle数据类型转为SR + * @param x + * @return + */ + public static SQLDataType transformOracleToSR(SQLDataType x) { + final String name = x.getName(); + final long nameHash = x.nameHashCode64(); + if (name == null) { + return x; + } + List arguments = x.getArguments(); + SQLDataType dataType; + if (nameHash == FnvHash.Constants.UROWID) { + int len = 4000; + if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); + if (arg0 instanceof SQLIntegerExpr) { + len = ((SQLIntegerExpr) arg0).getNumber().intValue(); + } + } + dataType = new SQLDataTypeImpl("varchar", len); + } else if (nameHash == FnvHash.Constants.ROWID) { + dataType = new SQLDataTypeImpl("char", 10); + + } else if (nameHash == FnvHash.Constants.BOOLEAN) { + dataType = new SQLDataTypeImpl("tinyint"); + + } else if (nameHash == FnvHash.Constants.INTEGER) { + dataType = new SQLDataTypeImpl("int"); + + } else if (nameHash == FnvHash.Constants.FLOAT + || nameHash == FnvHash.Constants.BINARY_FLOAT) { + dataType = new SQLDataTypeImpl("float"); + + } else if (nameHash == FnvHash.Constants.REAL + || nameHash == FnvHash.Constants.BINARY_DOUBLE + || nameHash == FnvHash.Constants.DOUBLE_PRECISION) { + dataType = new SQLDataTypeImpl("double"); + + } else if (nameHash == FnvHash.Constants.NUMBER) { + if (arguments.size() == 0) { + dataType = new SQLDataTypeImpl("decimal", 38); + } else { + SQLExpr arg0 = arguments.get(0); + + int precision, scale = 0; + if (arg0 instanceof SQLAllColumnExpr) { + precision = 9; + } else { + precision = ((SQLIntegerExpr) arg0).getNumber().intValue(); + } + + if (arguments.size() > 1) { + scale = ((SQLIntegerExpr) arguments.get(1)).getNumber().intValue(); + } + + if (scale > precision) { + if (arg0 instanceof SQLAllColumnExpr) { + precision = 19; + if (scale > precision) { + precision = scale; + } + } else { + precision = scale; + } + } + + if (scale == 0) { + if (precision < 3) { + dataType = new SQLDataTypeImpl("tinyint"); + } else if (precision < 5) { + dataType = new SQLDataTypeImpl("smallint"); + } else if (precision < 9) { + dataType = new SQLDataTypeImpl("int"); + } else if (precision <= 20) { + dataType = new SQLDataTypeImpl("bigint"); + } else { + dataType = new SQLDataTypeImpl("decimal", precision); + } + } else { + dataType = new SQLDataTypeImpl("decimal", precision, scale); + } + } + + } else if (nameHash == FnvHash.Constants.DEC + || nameHash == FnvHash.Constants.DECIMAL) { + + dataType = x.clone(); + dataType.setName("decimal"); + + int precision = 0; + if (arguments.size() > 0) { + precision = ((SQLIntegerExpr) arguments.get(0)).getNumber().intValue(); + } + + int scale = 0; + if (arguments.size() > 1) { + scale = ((SQLIntegerExpr) arguments.get(1)).getNumber().intValue(); + if (precision < scale) { + ((SQLIntegerExpr) dataType.getArguments().get(1)).setNumber(precision); + } + } + } else if (nameHash == FnvHash.Constants.RAW) { + int len; + + if (arguments.size() == 0) { + len = -1; + } else if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + + if (len == -1) { + dataType = new SQLDataTypeImpl("binary"); + } else if (len <= 255) { + dataType = new SQLDataTypeImpl("binary", len); + } else { + dataType = new SQLDataTypeImpl("varbinary", len); + } + } else if (nameHash == FnvHash.Constants.CHAR + || nameHash == FnvHash.Constants.CHARACTER) { + if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); + + int len; + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + + if (len <= 255) { + dataType = new SQLCharacterDataType("char", len); + } else { + dataType = new SQLCharacterDataType("varchar", len); + } + } else if (arguments.size() == 0) { + dataType = new SQLCharacterDataType("char"); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + + } else if (nameHash == FnvHash.Constants.NCHAR) { + if (arguments.size() == 1) { + SQLExpr arg0 = arguments.get(0); + + int len; + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + + if (len <= 255) { + dataType = new SQLCharacterDataType("nchar", len); + } else { + dataType = new SQLCharacterDataType("nvarchar", len); + } + } else if (arguments.size() == 0) { + dataType = new SQLCharacterDataType("nchar"); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + + } else if (nameHash == FnvHash.Constants.VARCHAR2) { + if (arguments.size() > 0) { + int len; + SQLExpr arg0 = arguments.get(0); + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + if(len >= 4000) { + dataType = new SQLCharacterDataType("text"); + }else { + dataType = new SQLCharacterDataType("varchar", len); + } + } else { + dataType = new SQLCharacterDataType("varchar"); + } + + } else if (nameHash == FnvHash.Constants.NVARCHAR2) { + if (arguments.size() > 0) { + int len; + SQLExpr arg0 = arguments.get(0); + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + dataType = new SQLCharacterDataType("nvarchar", len); + } else { + dataType = new SQLCharacterDataType("nvarchar"); + } + + } else if (nameHash == FnvHash.Constants.BFILE) { + dataType = new SQLCharacterDataType("varchar", 255); + + } else if (nameHash == FnvHash.Constants.DATE + || nameHash == FnvHash.Constants.TIMESTAMP) { + int len = -1; + if (arguments.size() > 0) { + SQLExpr arg0 = arguments.get(0); + if (arg0 instanceof SQLNumericLiteralExpr) { + len = ((SQLNumericLiteralExpr) arg0).getNumber().intValue(); + } else { + throw new UnsupportedOperationException(SQLUtils.toOracleString(x)); + } + } + + /*if (len >= 0) { + if (len > 6) { + len = 6; + } + dataType = new SQLDataTypeImpl("datetime", len); + } else {*/ + dataType = new SQLDataTypeImpl("datetime"); + /* }*/ + } else if (nameHash == FnvHash.Constants.BLOB + || nameHash == FnvHash.Constants.LONG_RAW) { + arguments.clear(); + dataType = new SQLDataTypeImpl("VARBINARY"); // + } else if (nameHash == FnvHash.Constants.CLOB + || nameHash == FnvHash.Constants.NCLOB + || nameHash == FnvHash.Constants.LONG + || nameHash == FnvHash.Constants.XMLTYPE) { + arguments.clear(); + dataType = new SQLCharacterDataType("VARBINARY"); + //SR中不支持BLOB CLOB 统一转为VARBINARY 不指定长度 默认的最大值为1048576 字节=1MB + } else { + dataType = x; + } + + if (dataType != x) { + dataType.setParent(x.getParent()); + } + + return dataType; + } + } diff --git a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/OracleToStarrocksOutputVisitor.java b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/OracleToStarrocksOutputVisitor.java new file mode 100644 index 0000000..04d6a39 --- /dev/null +++ b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/OracleToStarrocksOutputVisitor.java @@ -0,0 +1,635 @@ +package com.raysonfang.sqltranslator.sql.dialect.starrocks; + +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.ast.expr.*; +import com.alibaba.druid.sql.ast.statement.*; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlPrimaryKey; +import com.alibaba.druid.sql.dialect.mysql.ast.MySqlUnique; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableModifyColumn; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlAlterTableOption; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlDeleteStatement; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSysdateExpr; +import com.alibaba.druid.sql.dialect.oracle.ast.stmt.*; +import com.alibaba.druid.sql.dialect.oracle.visitor.OracleOutputVisitor; +import com.alibaba.druid.sql.parser.SQLParserUtils; +import com.alibaba.druid.sql.parser.Token; +import com.alibaba.druid.util.FnvHash; +import com.alibaba.druid.util.JdbcConstants; +import com.raysonfang.sqltranslator.sql.dialect.mysql.util.MySqlUtil; +import com.raysonfang.sqltranslator.sql.dialect.oracle.function.OracleToMySqlFunctionTransform; +import com.raysonfang.sqltranslator.sql.dialect.oracle.util.OracleSQLDataTypeTransformUtil; +import com.raysonfang.sqltranslator.sql.dialect.starrocks.function.OracleToStarrocksFunctionTransform; +import com.raysonfang.sqltranslator.util.MapCacheUtil; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.Optional; + +/** + * oracle的sql 转换成 mysql的sql语法 + * + * @author rayson.fang + * @date 2021/02/05 12:11 + **/ +public class OracleToStarrocksOutputVisitor extends OracleOutputVisitor { + +// private final OracleToMySqlFunctionTransform functionTransform = new OracleToMySqlFunctionTransform(); + private final OracleToStarrocksFunctionTransform functionTransform = new OracleToStarrocksFunctionTransform(); + // 目标数据库类型 + private final DbType distDbType = DbType.mysql; + + public OracleToStarrocksOutputVisitor(Appendable appender, boolean printPostSemi){ + super(appender, printPostSemi); + } + + public OracleToStarrocksOutputVisitor(Appendable appender){ + super(appender); + } + + @Override + public boolean visit(OracleSelectQueryBlock x) { + boolean parentIsSelectStatment = false; + { + if (x.getParent() instanceof SQLSelect) { + SQLSelect select = (SQLSelect) x.getParent(); + if (select.getParent() instanceof SQLSelectStatement || select.getParent() instanceof SQLSubqueryTableSource) { + parentIsSelectStatment = true; + } + } + } + + if (!parentIsSelectStatment) { + return super.visit(x); + } + + Optional.ofNullable(x.getSelectList()).orElse(new ArrayList<>()).stream().forEach(c->{ + functionTransform.transformFunctionExpr(c.getExpr()); + }); + + if (x.getWhere() instanceof SQLBinaryOpExpr // + && x.getFrom() instanceof SQLSubqueryTableSource // + ) { + int rownum; + SQLVariantRefExpr rownumRef=null; + String ident; + SQLBinaryOpExpr where = (SQLBinaryOpExpr) x.getWhere(); + if (where.getRight() instanceof SQLIntegerExpr && where.getLeft() instanceof SQLIdentifierExpr) { + rownum = ((SQLIntegerExpr) where.getRight()).getNumber().intValue(); + ident = ((SQLIdentifierExpr) where.getLeft()).getName(); + } else if(where.getRight() instanceof SQLVariantRefExpr && where.getLeft() instanceof SQLIdentifierExpr) { + rownum = 0; + rownumRef = (SQLVariantRefExpr) where.getRight(); + ident = ((SQLIdentifierExpr) where.getLeft()).getName(); + } else { + return super.visit(x); + } + SQLSelect select = ((SQLSubqueryTableSource) x.getFrom()).getSelect(); + SQLSelectQueryBlock queryBlock = null; + SQLSelect subSelect = null; + SQLBinaryOpExpr subWhere = null; + boolean isSubQueryRowNumMapping = false; + + if (select.getQuery() instanceof SQLSelectQueryBlock) { + queryBlock = (SQLSelectQueryBlock) select.getQuery(); + if (queryBlock.getWhere() instanceof SQLBinaryOpExpr) { + subWhere = (SQLBinaryOpExpr) queryBlock.getWhere(); + } + + for (SQLSelectItem selectItem : queryBlock.getSelectList()) { + if (isRowNumber(selectItem.getExpr())) { + if (where.getLeft() instanceof SQLIdentifierExpr + && ((SQLIdentifierExpr) where.getLeft()).getName().equals(selectItem.getAlias())) { + isSubQueryRowNumMapping = true; + } + } + } + + SQLTableSource subTableSource = queryBlock.getFrom(); + if (subTableSource instanceof SQLSubqueryTableSource) { + subSelect = ((SQLSubqueryTableSource) subTableSource).getSelect(); + } + } + + if ("ROWNUM".equalsIgnoreCase(ident)) { + SQLBinaryOperator op = where.getOperator(); + Integer limit = null; + if (op == SQLBinaryOperator.LessThanOrEqual) { + //limit1 = rownumRef.toString(); + limit = rownum; + } else if (op == SQLBinaryOperator.LessThan) { + //limit1 = rownumRef.toString() + "-1"; + limit = rownum - 1; + } + + if (limit != null) { + select.accept(this); + println(); + print0(ucase ? "LIMIT " : "limit "); + print(rownumRef != null ? rownumRef.getName() : String.valueOf(limit)); + return false; + } + } else if (isSubQueryRowNumMapping) { + SQLBinaryOperator op = where.getOperator(); + SQLBinaryOperator subOp = subWhere.getOperator(); + + if (isRowNumber(subWhere.getLeft()) // + && subWhere.getRight() instanceof SQLIntegerExpr) { + + int subRownum = ((SQLIntegerExpr) subWhere.getRight()).getNumber().intValue(); + + Integer offset = null; + if (op == SQLBinaryOperator.GreaterThanOrEqual) { + offset = rownum + 1; + } else if (op == SQLBinaryOperator.GreaterThan) { + offset = rownum; + } + + if (offset != null) { + Integer limit = null; + if (subOp == SQLBinaryOperator.LessThanOrEqual) { + limit = subRownum - offset; + } else if (subOp == SQLBinaryOperator.LessThan) { + limit = subRownum - 1 - offset; + } + + if (limit != null) { + subSelect.accept(this); + println(); + print0(ucase ? "LIMIT " : "limit "); + print("?"); + print0(", "); + print("?"); + return false; + } + } + }else if(isRowNumber(subWhere.getLeft()) // + && subWhere.getRight() instanceof SQLVariantRefExpr) { + subSelect.accept(this); + println(); + print0(ucase ? "LIMIT " : "limit "); + print(rownumRef.getName()); + print0(", "); + print(((SQLVariantRefExpr) subWhere.getRight()).getName()); + return false; + } + } + } + + return super.visit(x); + } + + static boolean isRowNumber(SQLExpr expr) { + if (expr instanceof SQLIdentifierExpr) { + return ((SQLIdentifierExpr) expr) + .hashCode64() == FnvHash.Constants.ROWNUM; + } + + return false; + } + + @Override + public boolean visit(SQLDropTableStatement x) { + x.setDbType(DbType.mysql); + x.setIfExists(true); + return super.visit(x); + } + + @Override + public boolean visit(SQLBlockStatement x) { + for(SQLStatement statement : x.getStatementList()) { + if (statement instanceof SQLIfStatement) { + for (SQLStatement s : ((SQLIfStatement) (statement)).getStatements()) { + if (s instanceof OracleExecuteImmediateStatement) { + String sql = ((OracleExecuteImmediateStatement) s) + .getDynamicSql().toString().toLowerCase(); + if (sql.contains("drop table")) { + println(); + print0(sql.replace("drop table", "drop table if exists ").replaceAll("'","")); + return false; + } + } + } + } + } + return super.visit(x); + } + + @Override + public boolean visit(SQLAlterTableStatement x) { + for (SQLAlterTableItem item : x.getItems()) { + if(item instanceof SQLAlterTableRename) { + ((SQLAlterTableStatement) item.getParent()).setDbType(distDbType); + String rename = ""; + if(x.isAfterSemi()) { + rename = x.toString().substring(0, x.toString().length() - 1); + } else { + rename = x.toString(); + } + println(); + print0(rename); + return false; + } + } + + return super.visit(x); + } + + @Override + public boolean visit(OracleInsertStatement x) { + /*print(";");*/ + x.getTableSource().setExpr(x.getTableSource().getName().toString().replaceAll("\"","")); + x.getColumns().forEach(c->{ + ((SQLIdentifierExpr)c).setName(((SQLIdentifierExpr)c).getName().replaceAll("\"", "")); + if(MySqlUtil.containsKeyWords(((SQLIdentifierExpr)c).getName())) { + ((SQLIdentifierExpr)c).setName("`"+((SQLIdentifierExpr)c).getName()+"`"); + } + }); + Optional.ofNullable(x.getValuesList()).orElse(new ArrayList<>()).forEach(e -> { + Optional.ofNullable(e.getValues()).orElse(new ArrayList<>()).forEach(c -> { + functionTransform.transformFunctionExpr(c); + }); + }); + println(); + return super.visit(x); + } + + @Override + public boolean visit(OracleUpdateStatement x) { + println(); + Optional.ofNullable(x.getItems()).orElse(new ArrayList<>()).forEach(expr -> { + functionTransform.transformFunctionExpr(expr.getValue()); + }); + SQLExpr where = x.getWhere(); + if (!ObjectUtils.isEmpty(where)) { + Optional.ofNullable(where.getChildren()).orElse(new ArrayList<>()).forEach(e -> { + functionTransform.sqlWhereList(e); + }); + } + /*print(";");*/ + return super.visit(x); + } + + @Override + public boolean visit(OracleCreateTableStatement x) { + MySqlCreateTableStatement mySqlCreateTableStatement = new MySqlCreateTableStatement(); + mySqlCreateTableStatement.setTableSource(x.getTableSource()); + String tableName = x.getTableSource().getName().getSimpleName(); + for(SQLTableElement sqlTableElement : x.getTableElementList()){ + if( sqlTableElement instanceof SQLColumnDefinition) { + SQLColumnDefinition sqlColumnDefinition = ((SQLColumnDefinition)sqlTableElement); + String columnName = sqlColumnDefinition.getName().getSimpleName().replaceAll("\"", ""); + if(MySqlUtil.containsKeyWords(columnName)){ + sqlColumnDefinition.setName("`"+columnName+"`"); + } + // 类型转换在这边完成 +// sqlColumnDefinition.setDataType(OracleSQLDataTypeTransformUtil.transformOracleToMySql(SQLParserUtils.createExprParser(sqlColumnDefinition.getDataType().toString(), DbType.oracle).parseDataType())); + sqlColumnDefinition.setDataType(OracleSQLDataTypeTransformUtil.transformOracleToSR(SQLParserUtils.createExprParser(sqlColumnDefinition.getDataType().toString(), DbType.oracle).parseDataType())); + + if(sqlColumnDefinition.getDefaultExpr() != null) { + // 获取建表语句中的default关键字 + SQLExpr expr = sqlColumnDefinition.getDefaultExpr(); + if(expr instanceof SQLMethodInvokeExpr) { + SQLMethodInvokeExpr sqlMethodInvokeExpr = (SQLMethodInvokeExpr) expr; + final long nameHashCode64 = sqlMethodInvokeExpr.methodNameHashCode64(); + if (nameHashCode64 == FnvHash.Constants.SYS_GUID || nameHashCode64 == FnvHash.Constants.TO_CHAR) { + sqlColumnDefinition.setDefaultExpr(null); + }else { + functionTransform.methodInvoke((SQLMethodInvokeExpr) sqlColumnDefinition.getDefaultExpr()); + } + + }else if(expr instanceof SQLIdentifierExpr) { + SQLIdentifierExpr sqlIdentifierExpr = (SQLIdentifierExpr) expr; + final long nameHashCode64 = sqlIdentifierExpr.nameHashCode64(); + if(nameHashCode64 == FnvHash.Constants.SYSTIMESTAMP) { + SQLIdentifierExpr xx = sqlIdentifierExpr.clone(); + xx.setName("CURRENT_TIMESTAMP"); + xx.setParent(sqlIdentifierExpr.getParent()); + sqlColumnDefinition.setDefaultExpr(xx); + if(sqlColumnDefinition.getColumnName().contains("UPDATE_TIME") + || sqlColumnDefinition.getColumnName().contains("MDFY_TIME")) { + sqlColumnDefinition.setOnUpdate(xx); + } + } + }else if(expr instanceof OracleSysdateExpr) { + SQLIdentifierExpr xx = new SQLIdentifierExpr("CURRENT_TIMESTAMP"); + xx.setParent(expr.getParent()); + sqlColumnDefinition.setDefaultExpr(xx); + if(sqlColumnDefinition.getColumnName().contains("UPDATE_TIME") + || sqlColumnDefinition.getColumnName().contains("MDFY_TIME")) { + sqlColumnDefinition.setOnUpdate(xx); + } + } + } + //转为目标数据库的类型 + sqlColumnDefinition.setDbType(distDbType); + MapCacheUtil.getInstance().addCacheData(tableName.toUpperCase() + ":" + columnName.toUpperCase(), sqlColumnDefinition.toString().replaceAll(sqlColumnDefinition.getColumnName(), "")); + mySqlCreateTableStatement.getTableElementList().add(sqlColumnDefinition); + }else if(sqlTableElement instanceof OraclePrimaryKey){ + MySqlPrimaryKey mySqlPrimaryKey = new MySqlPrimaryKey(); + ((OraclePrimaryKey) sqlTableElement).cloneTo(mySqlPrimaryKey); + mySqlCreateTableStatement.getTableElementList().add(mySqlPrimaryKey); + }else if(sqlTableElement instanceof OracleUnique) { + MySqlUnique mySqlUnique = new MySqlUnique(); + ((OracleUnique) sqlTableElement).cloneTo(mySqlUnique); + mySqlCreateTableStatement.getTableElementList().add(mySqlUnique); + } + } + if(Objects.nonNull(x.getSelect())){ + x.setParent(mySqlCreateTableStatement); + mySqlCreateTableStatement.setSelect(x.getSelect()); + } + println(); + print(mySqlCreateTableStatement.toString()); + /*print(";");*/ + return false; + } + + @Override + public boolean visit(SQLColumnDefinition x) { + if(x.getParent() instanceof SQLAlterTableAddColumn || x.getParent() instanceof OracleAlterTableModify) { + String columnName = x.getName().getSimpleName().replaceAll("\"", ""); + if(MySqlUtil.containsKeyWords(columnName)){ + x.setName("`"+columnName+"`"); + } +// x.setDataType(OracleSQLDataTypeTransformUtil.transformOracleToMySql(SQLParserUtils.createExprParser(x.getDataType().toString(), DbType.oracle).parseDataType())); + x.setDataType(OracleSQLDataTypeTransformUtil.transformOracleToSR(SQLParserUtils.createExprParser(x.getDataType().toString(), DbType.oracle).parseDataType())); + if(x.getDefaultExpr() != null) { + SQLExpr expr = x.getDefaultExpr(); + if(expr instanceof SQLMethodInvokeExpr) { + SQLMethodInvokeExpr sqlMethodInvokeExpr = (SQLMethodInvokeExpr) expr; + final long nameHashCode64 = sqlMethodInvokeExpr.methodNameHashCode64(); + if (nameHashCode64 == FnvHash.Constants.SYS_GUID || nameHashCode64 == FnvHash.Constants.TO_CHAR) { + x.setDefaultExpr(null); + }else { + functionTransform.methodInvoke((SQLMethodInvokeExpr) x.getDefaultExpr()); + } + + }else if(expr instanceof SQLIdentifierExpr) { + SQLIdentifierExpr sqlIdentifierExpr = (SQLIdentifierExpr) expr; + final long nameHashCode64 = sqlIdentifierExpr.nameHashCode64(); + if(nameHashCode64 == FnvHash.Constants.SYSTIMESTAMP) { + SQLIdentifierExpr xx = sqlIdentifierExpr.clone(); + xx.setName("CURRENT_TIMESTAMP"); + xx.setParent(sqlIdentifierExpr.getParent()); + x.setDefaultExpr(xx); + if(x.getColumnName().contains("UPDATE_TIME") + || x.getColumnName().contains("MDFY_TIME")) { + x.setOnUpdate(xx); + } + } + }else if(expr instanceof OracleSysdateExpr) { + SQLIdentifierExpr xx = new SQLIdentifierExpr("CURRENT_TIMESTAMP"); + xx.setParent(expr.getParent()); + x.setDefaultExpr(xx); + if(x.getColumnName().contains("UPDATE_TIME") + || x.getColumnName().contains("MDFY_TIME")) { + x.setOnUpdate(xx); + } + } + } + x.setDbType(distDbType); + if((x.getParent().getParent()) instanceof SQLAlterTableStatement ) { + String tableName = ((SQLAlterTableStatement)(x.getParent().getParent())).getTableName(); + MapCacheUtil.getInstance().addCacheData(tableName.toUpperCase() + ":" + columnName.toUpperCase(), x.toString().replaceAll(x.getColumnName(), "")); + } + } + return super.visit(x); + } + + @Override + public boolean visit(OracleDeleteStatement x) { + println(); + SQLExpr where = x.getWhere(); + if (!ObjectUtils.isEmpty(where)) { + if(where instanceof SQLExistsExpr) { + SQLSelect sqlSelect = ((SQLExistsExpr) where).getSubQuery(); + SQLSelectQueryBlock oracleSelectQueryBlock = sqlSelect.getQueryBlock(); + SQLTableSource oracleSelectTableReference = oracleSelectQueryBlock.getFrom(); + + MySqlDeleteStatement mySqlDeleteStatement = new MySqlDeleteStatement(); + SQLExprTableSource sqlExprTableSource = new SQLExprTableSource(); + sqlExprTableSource.setExpr(x.getAlias()); + sqlExprTableSource.setParent(mySqlDeleteStatement); + + mySqlDeleteStatement.setTableSource(sqlExprTableSource); + + SQLJoinTableSource sqlJoinTableSource1 = new SQLJoinTableSource(); + SQLExprTableSource leftTable = new SQLExprTableSource(); + leftTable.setExpr(x.getTableName()); + leftTable.setParent(sqlJoinTableSource1); + leftTable.setAlias(x.getAlias()); + sqlJoinTableSource1.setLeft(leftTable); + sqlJoinTableSource1.setJoinType(SQLJoinTableSource.JoinType.COMMA); + SQLExprTableSource rightTable = new SQLExprTableSource(); + rightTable.setAlias(oracleSelectTableReference.getAlias()); + rightTable.setExpr(((OracleSelectTableReference)oracleSelectTableReference).getName()); + rightTable.setParent(sqlJoinTableSource1); + sqlJoinTableSource1.setRight(rightTable); + sqlJoinTableSource1.setParent(mySqlDeleteStatement); + mySqlDeleteStatement.setUsing(sqlJoinTableSource1); + if(oracleSelectQueryBlock.getWhere() instanceof SQLBinaryOpExpr) { + SQLBinaryOpExpr sqlBinaryOpExpr = (SQLBinaryOpExpr) oracleSelectQueryBlock.getWhere(); + if(sqlBinaryOpExpr.getOperator() == SQLBinaryOperator.Equality) { + if(sqlBinaryOpExpr.getLeft() instanceof SQLBinaryOpExpr) { + if (((SQLBinaryOpExpr)sqlBinaryOpExpr.getLeft()).getOperator() == SQLBinaryOperator.Concat) { + String str = sqlBinaryOpExpr.getLeft().toString(); + SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(String.format("concat(%s)", str.replaceAll("\\|\\|", ","))); + sqlBinaryOpExpr.setLeft(sqlIdentifierExpr); + sqlIdentifierExpr.setParent(sqlBinaryOpExpr); + } + } + if(sqlBinaryOpExpr.getRight() instanceof SQLBinaryOpExpr) { + if (((SQLBinaryOpExpr)sqlBinaryOpExpr.getRight()).getOperator() == SQLBinaryOperator.Concat) { + String str = sqlBinaryOpExpr.getRight().toString(); + SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(String.format("concat(%s)", str.replaceAll("\\|\\|", ","))); + sqlBinaryOpExpr.setRight(sqlIdentifierExpr); + sqlIdentifierExpr.setParent(sqlBinaryOpExpr); + } + } + + } + mySqlDeleteStatement.setWhere(sqlBinaryOpExpr); + } + mySqlDeleteStatement.setDbType(distDbType); + println(); + print(mySqlDeleteStatement.toString()); + return false; + } + Optional.ofNullable(where.getChildren()).orElse(new ArrayList<>()).forEach(expr -> { + functionTransform.sqlWhereList(expr); + }); + } + /*print(";");*/ + return super.visit(x); + } + + @Override + public boolean visit(OracleCreateIndexStatement x) { + if(Token.UNIQUE.name.equals(x.getType())) { + SQLCreateIndexStatement sqlCreateIndexStatement = new SQLCreateIndexStatement(); + sqlCreateIndexStatement.setTable(x.getTable()); + sqlCreateIndexStatement.setName(x.getName()); + x.getItems().stream().forEach(item->sqlCreateIndexStatement.addItem(item)); + sqlCreateIndexStatement.setDbType(distDbType); + println(); + print(sqlCreateIndexStatement.toString()); + /* print(";");*/ + return false; + } + return super.visit(x); + } + + @Override + public boolean visit(SQLCreateSequenceStatement x) { + + return false; + } + + @Override + public boolean visit(SQLCommentStatement x) { + if(SQLCommentStatement.Type.TABLE.name().equals(x.getType().name())){ + String tableName = x.getOn().getName().getSimpleName(); + SQLAlterTableStatement sqlAlterStatement = new SQLAlterTableStatement(); + SQLExprTableSource sqlExprTableSource = new SQLExprTableSource(); + sqlExprTableSource.setExpr(tableName); + sqlAlterStatement.setTableSource(sqlExprTableSource); + sqlAlterStatement.setDbType(distDbType); + MySqlAlterTableOption sqlAlterTableOption = new MySqlAlterTableOption(); + sqlAlterTableOption.setName("COMMENT"); + sqlAlterTableOption.setValue(x.getComment()); + sqlAlterTableOption.setParent(sqlAlterStatement); + sqlAlterStatement.addItem(sqlAlterTableOption); + println(); + print(sqlAlterStatement.toString()); + /*print(";");*/ + return false; + }else if(SQLCommentStatement.Type.COLUMN.name().equals(x.getType().name())) { + String columnName = x.getOn().getName().getSimpleName().replaceAll("\"", ""); + + String tableName = ((SQLPropertyExpr)x.getOn().getExpr()).getOwner().toString(); + SQLColumnDefinition sqlColumnDefinition = new SQLColumnDefinition(); + sqlColumnDefinition.setDbType(JdbcConstants.MYSQL); + if(MySqlUtil.containsKeyWords(columnName)){ + sqlColumnDefinition.setName("`"+columnName+"`"); + }else{ + sqlColumnDefinition.setName(columnName); + } + sqlColumnDefinition.setDataType(new SQLCharacterDataType((String)MapCacheUtil.getInstance().getCacheData(tableName.toUpperCase()+":"+columnName.toUpperCase()))); + sqlColumnDefinition.setComment(x.getComment()); + MySqlAlterTableModifyColumn mySqlAlterTableModifyColumn = new MySqlAlterTableModifyColumn(); + mySqlAlterTableModifyColumn.setNewColumnDefinition(sqlColumnDefinition); + + SQLAlterTableStatement sqlAlterTableStatement = new SQLAlterTableStatement(distDbType); + SQLExprTableSource sqlExprTableSource = new SQLExprTableSource(); + sqlExprTableSource.setExpr(tableName); + sqlAlterTableStatement.setTableSource(sqlExprTableSource); + sqlAlterTableStatement.addItem(mySqlAlterTableModifyColumn); + println(); + print(sqlAlterTableStatement.toString()); + return false; + } + return super.visit(x); + } + + @Override + public boolean visit(SQLCommitStatement x) { + println(); + return super.visit(x); + } + + @Override + public boolean visit(SQLWhileStatement x){ + x.setAfterSemi(true); + return super.visit(x); + } + + @Override + public boolean visit(SQLCreateTriggerStatement x) { + x.setDbType(distDbType); + SQLBlockStatement body = (SQLBlockStatement)x.getBody(); + body.setDbType(distDbType); + for(SQLStatement sqlStatement : body.getStatementList()){ + if (sqlStatement instanceof SQLIfStatement) { + ((SQLIfStatement) sqlStatement).setDbType(distDbType); + } + } + super.visit(x); + println(SQLUtils.toMySqlString(x)); + return false; + } + + @Override + public boolean visit(SQLVariantRefExpr x){ + if(":new".equalsIgnoreCase(x.getName()) || ":old".equalsIgnoreCase(x.getName())) { + x.setName(x.getName().replace(":", "").toLowerCase()); + } + return super.visit(x); + } + + @Override + public boolean visit(SQLSelectOrderByItem x) { + if(Objects.nonNull(x.getNullsOrderType())) { // 处理nulls last || nulls first + Integer first = 0, last = 0; + if(SQLSelectOrderByItem.NullsOrderType.NullsFirst.name().equals(x.getNullsOrderType().name())) { + first = 1; + } else { + last = 1; + } + SQLMethodInvokeExpr sqlMethodInvokeExpr = new SQLMethodInvokeExpr(); + sqlMethodInvokeExpr.setMethodName("IF"); + SQLMethodInvokeExpr sqlMethodInvokeExpr1 = new SQLMethodInvokeExpr(); + sqlMethodInvokeExpr1.setMethodName("ISNULL"); + sqlMethodInvokeExpr1.addArgument(x.getExpr()); + sqlMethodInvokeExpr1.setParent(sqlMethodInvokeExpr); + sqlMethodInvokeExpr.addArgument(sqlMethodInvokeExpr1); + SQLIntegerExpr sqlIntegerExpr = new SQLIntegerExpr(); + sqlIntegerExpr.setNumber(first); + sqlIntegerExpr.setParent(sqlMethodInvokeExpr); + SQLIntegerExpr sqlIntegerExpr1 = new SQLIntegerExpr(); + sqlIntegerExpr1.setNumber(last); + sqlIntegerExpr1.setParent(sqlMethodInvokeExpr); + sqlMethodInvokeExpr.addArgument(sqlIntegerExpr); + sqlMethodInvokeExpr.addArgument(sqlIntegerExpr1); + + x.setNullsOrderType(null); + x.setExpr(sqlMethodInvokeExpr); + } + return super.visit(x); + } + + @Override + public boolean visit(SQLMethodInvokeExpr x) { + functionTransform.methodInvoke(x); + return super.visit(x); + } + + @Override + public boolean visit(SQLBinaryOpExpr x) { + x.setDbType(distDbType); + if(x.getOperator() == SQLBinaryOperator.Equality) { + if(x.getLeft() instanceof SQLBinaryOpExpr) { + if (((SQLBinaryOpExpr)x.getLeft()).getOperator() == SQLBinaryOperator.Concat) { + String str = x.getLeft().toString(); + SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(String.format("concat(%s)", str.replaceAll("\\|\\|", ","))); + print(sqlIdentifierExpr.toString()); + return false; + } + } + if(x.getRight() instanceof SQLBinaryOpExpr) { + if (((SQLBinaryOpExpr)x.getRight()).getOperator() == SQLBinaryOperator.Concat) { + String str = x.getRight().toString(); + SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(String.format("concat(%s)", str.replaceAll("\\|\\|", ","))); + print(sqlIdentifierExpr.toString()); + return false; + } + } + + } + return super.visit(x); + } +} diff --git a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/function/OracleToStarrocksFunctionTransform.java b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/function/OracleToStarrocksFunctionTransform.java new file mode 100644 index 0000000..73c72bf --- /dev/null +++ b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/function/OracleToStarrocksFunctionTransform.java @@ -0,0 +1,440 @@ +package com.raysonfang.sqltranslator.sql.dialect.starrocks.function; + +import com.alibaba.druid.sql.ast.SQLDataType; +import com.alibaba.druid.sql.ast.SQLExpr; +import com.alibaba.druid.sql.ast.SQLObject; +import com.alibaba.druid.sql.ast.SQLOrderBy; +import com.alibaba.druid.sql.ast.expr.*; +import com.alibaba.druid.sql.ast.statement.SQLSelectItem; +import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleSysdateExpr; +import com.alibaba.druid.util.StringUtils; +import com.raysonfang.sqltranslator.sql.dialect.mysql.util.MySqlUtil; +import com.raysonfang.sqltranslator.sql.dialect.oracle.function.OracleFunction; +import com.raysonfang.sqltranslator.sql.dialect.oracle.util.OracleSQLDataTypeTransformUtil; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * oracle函数 转换成 mysql函数 + * 模仿修改为oracle到 starrocks函数 + * + * @author rayson.fang + * @date 2021/02/05 12:38 + **/ +public class OracleToStarrocksFunctionTransform implements OracleFunction { + + @Override + public void transformFunctionExpr(SQLExpr sqlExpr) { + if (sqlExpr instanceof SQLMethodInvokeExpr) { //函数 + forEachMethod(sqlExpr); + alias(sqlExpr); + } else if (sqlExpr instanceof SQLIdentifierExpr) { //字段 + String name = ((SQLIdentifierExpr) sqlExpr).getName().toUpperCase(); + alias(sqlExpr); + if ("SYSTIMESTAMP".equalsIgnoreCase(name)) { + ((SQLIdentifierExpr) sqlExpr).setName("CURRENT_TIMESTAMP"); + } else { + ((SQLIdentifierExpr) sqlExpr).setName(formatKeyField(name)); + } + } else if (sqlExpr instanceof SQLPropertyExpr) { //参数 + String name = ((SQLPropertyExpr) sqlExpr).getName(); + alias(sqlExpr); + ((SQLPropertyExpr) sqlExpr).setName(formatKeyField(name.toUpperCase())); + } else if (sqlExpr instanceof SQLCharExpr) { // 字符 + alias(sqlExpr); + } else if (sqlExpr instanceof SQLBinaryOpExpr) { + SQLBinaryOperator operator = ((SQLBinaryOpExpr) sqlExpr).getOperator(); + Optional.ofNullable(sqlExpr.getChildren()).orElse(new ArrayList<>()).forEach(e -> { + if (e instanceof SQLMethodInvokeExpr) { //函数 + // forEachMethod((SQLMethodInvokeExpr)e); + methodInvoke((SQLMethodInvokeExpr) e); + } + }); + + if (SQLBinaryOperator.Concat == operator) { + String str = sqlExpr.toString(); + SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr(String.format("concat(%s)", str.replaceAll("\\|\\|", ","))); + sqlIdentifierExpr.setParent(sqlExpr.getParent()); + SQLObject parent = sqlExpr.getParent(); + if (parent instanceof SQLSelectItem) { + ((SQLSelectItem) parent).setExpr(sqlIdentifierExpr); + } + } else if (sqlExpr instanceof SQLAggregateExpr) { + methodInvoke((SQLMethodInvokeExpr) sqlExpr); + } + + alias(sqlExpr); + + } else if (sqlExpr instanceof SQLCastExpr) { + SQLDataType dataType = ((SQLCastExpr) sqlExpr).getDataType(); + if (!ObjectUtils.isEmpty(dataType) && "timestamp".equalsIgnoreCase(dataType.getName())) { + dataType.setName("datetime"); + } + } + } + + @Override + public String concat(SQLObject sqlObject) { + StringBuilder sb = new StringBuilder(); + if (sqlObject instanceof SQLBinaryOpExpr) { + SQLExpr left = ((SQLBinaryOpExpr) sqlObject).getLeft(); + sb.append(concat(left)); + SQLExpr right = ((SQLBinaryOpExpr) sqlObject).getRight(); + sb.append(concat(right)); + } + if (sqlObject instanceof SQLIdentifierExpr) { + String name = ((SQLIdentifierExpr) sqlObject).getName(); + sb.append(name); + sb.append(" "); + } else if (sqlObject instanceof SQLCharExpr) { + String name = ((SQLCharExpr) sqlObject).getText(); + sb.append(" '"); + sb.append(name); + sb.append("' "); + } + return sb.toString(); + } + + @Override + public void listagg(SQLMethodInvokeExpr expr) { + if (expr instanceof SQLAggregateExpr) { + String methodName = expr.getMethodName(); + if ("listagg".equalsIgnoreCase(methodName)) { + List children = expr.getChildren(); + StringBuffer sb = new StringBuffer(); + StringBuffer field = new StringBuffer(); + StringBuffer fieldVal = new StringBuffer(); + StringBuffer orderBy = new StringBuffer(); + children.forEach(e -> { + if (e instanceof SQLPropertyExpr) { + field.append(((SQLPropertyExpr) e).getName()); + } else if (e instanceof SQLCharExpr) { + fieldVal.append(" separator '" + ((SQLCharExpr) e).getText()); + fieldVal.append("'"); + } else if (e instanceof SQLOrderBy) { + ((SQLOrderBy) e).getItems().forEach(c -> { + orderBy.append(c.getExpr().toString()); + }); + } + + }); + sb.append("group_concat("); + sb.append(field); + sb.append(" "); + if (orderBy.toString().length() > 0) { + sb.append(" order by "); + sb.append(orderBy); + } + + sb.append(" "); + sb.append(fieldVal); + sb.append(")"); + identifierExpr(sb.toString(), expr); + } + } + } + + @Override + public void sys_guid(SQLMethodInvokeExpr expr) { + identifierExpr("md5(uuid())", expr); + } + + @Override + public void to_char(SQLMethodInvokeExpr expr) { + expr.setMethodName("date_format"); + SQLIdentifierExpr sqlCharExpr = null; + List children = expr.getChildren(); + for (int i = 0, listSize = children.size(); i < listSize; i++) { + SQLObject sqlObj = children.get(i); + if (sqlObj instanceof SQLCharExpr) { + String textStr = ((SQLCharExpr) sqlObj).getText(); + if (textStr.indexOf("99") < 0) { + ((SQLCharExpr) sqlObj).setText(formatDate(textStr)); + } else { + + sqlCharExpr = new SQLIdentifierExpr(String.valueOf(getRound(textStr))); + sqlCharExpr.setParent(sqlObj.getParent()); + children.set(i, sqlCharExpr); + expr.setMethodName("ROUND"); + } + } else { + String val = ""; + int length = 0; + if (sqlObj instanceof SQLIntegerExpr) { + val = ((SQLIntegerExpr) sqlObj).getValue().toString(); + length = val.length(); + } else if (sqlObj instanceof SQLNumberExpr) { + val = ((SQLNumberExpr) sqlObj).getNumber().toString(); + length = val.length(); + if ((i + 1) <= listSize) { + SQLObject sqlObjNext = children.get(i + 1); + if (sqlObjNext instanceof SQLCharExpr) { + continue; + } + } + + } else if (sqlObj instanceof SQLIdentifierExpr) { + if (sqlObj.getParent() instanceof SQLMethodInvokeExpr) { + if (((SQLMethodInvokeExpr) sqlObj.getParent()).getArguments().size() > 1) { + continue; + } + } + val = ((SQLIdentifierExpr) sqlObj).getName(); + length = 11; + } else if (sqlObj instanceof SQLVariantRefExpr) { + if ((((SQLVariantRefExpr) sqlObj).getChildren().size()) > 1) { + continue; + } + val = ((SQLVariantRefExpr) sqlObj).getName(); + length = 11; + }else if (sqlObj instanceof OracleSysdateExpr) { + children.set(i, new SQLIdentifierExpr("now()")); + continue; + } else { + continue; + } + sqlCharExpr = new SQLIdentifierExpr(String.format("%s AS CHAR(%d)", val, length)); + sqlCharExpr.setParent(sqlObj.getParent()); + children.set(i, sqlCharExpr); + expr.setMethodName("cast"); + } + } + } + + @Override + public void to_date(SQLMethodInvokeExpr expr) { + expr.setMethodName("str_to_date"); + List children = expr.getChildren(); + for (int i = 0, listSize = children.size(); i < listSize; i++) { + SQLObject sqlObj = children.get(i); + if (sqlObj instanceof SQLCharExpr) { + String textStr = ((SQLCharExpr) sqlObj).getText(); + ((SQLCharExpr) sqlObj).setText(formatDate(textStr)); + } + } + } + + @Override + public void to_timestamp(SQLMethodInvokeExpr expr) { + expr.setMethodName("str_to_date"); + List children = expr.getChildren(); + for (int i = 0, listSize = children.size(); i < listSize; i++) { + SQLObject sqlObj = children.get(i); + if (sqlObj instanceof SQLCharExpr) { + String textStr = ((SQLCharExpr) sqlObj).getText(); + ((SQLCharExpr) sqlObj).setText(formatDate(textStr)); + } + } + } + + @Override + public void to_number(SQLMethodInvokeExpr expr) { + expr.setMethodName("cast"); + List children = expr.getChildren(); + SQLIdentifierExpr sqlCharExpr = null; + for (int i = 0, listSize = children.size(); i < listSize; i++) { + SQLObject sqlObj = children.get(i); + if (sqlObj instanceof SQLCharExpr) { + String textStr = ((SQLCharExpr) sqlObj).getText(); + sqlCharExpr = new SQLIdentifierExpr(String.format("'%s' AS SIGNED", textStr)); + sqlCharExpr.setParent(sqlObj.getParent()); + children.set(i, sqlCharExpr); + } else if (sqlObj instanceof SQLIdentifierExpr) { + String name = ((SQLIdentifierExpr) sqlObj).getName(); + ((SQLIdentifierExpr) sqlObj).setName(String.format("%s AS SIGNED", name)); + } else if (sqlObj instanceof SQLVariantRefExpr) { + String name = ((SQLVariantRefExpr) sqlObj).getName(); + ((SQLVariantRefExpr) sqlObj).setName(String.format("%s AS SIGNED", name)); + } + } + } + + @Override + public void trunc(SQLMethodInvokeExpr expr) { + List children = expr.getChildren(); + SQLIdentifierExpr sqlIdentifierExpr = null; + expr.setMethodName("truncate"); + for (int i = 0, listSize = children.size(); i < listSize; i++) { + SQLObject sqlObj = children.get(i); + if (sqlObj instanceof OracleSysdateExpr) { + sqlIdentifierExpr = new SQLIdentifierExpr(); + sqlIdentifierExpr.setName("now(), '%Y-%m-%d'"); + sqlIdentifierExpr.setParent(sqlObj.getParent()); + children.set(i, sqlIdentifierExpr); + expr.setMethodName("date_format"); + } else if (sqlObj instanceof SQLIdentifierExpr) { + String name = ((SQLIdentifierExpr) sqlObj).getName(); + if (name.toLowerCase().indexOf("time") > 0) { // 如果是一个字段 必须包含time + sqlIdentifierExpr = new SQLIdentifierExpr(); + sqlIdentifierExpr.setName(name + ", '%Y-%m-%d'"); + sqlIdentifierExpr.setParent(sqlObj.getParent()); + children.set(i, sqlIdentifierExpr); + expr.setMethodName("date_format"); + } + // 其他占卜做处理 + } else if (sqlObj instanceof SQLNumberExpr) { + Number number = ((SQLNumberExpr) sqlObj).getNumber(); + sqlIdentifierExpr = new SQLIdentifierExpr(); + if (number.intValue() > 0) { + String num = "0"; + List arguments = ((SQLMethodInvokeExpr) sqlObj.getParent()).getArguments(); + if (arguments != null && arguments.size() == 2) { + if (arguments.get(1) instanceof SQLIntegerExpr) { + num = ((SQLIntegerExpr) arguments.get(1)).getNumber().toString(); + } + } + sqlIdentifierExpr.setName(String.format("%s, %s", number.toString(), num)); + expr.setMethodName("truncate"); + } else { + sqlIdentifierExpr.setName(String.format("%s as SIGNED", number.toString())); + expr.setMethodName("cast"); + } + sqlIdentifierExpr.setParent(sqlObj.getParent()); + children.set(i, sqlIdentifierExpr); + } else if (sqlObj instanceof SQLCharExpr) { + String text = ((SQLCharExpr) sqlObj).getText(); + ((SQLCharExpr) sqlObj).setText(formatDate(text)); + expr.setMethodName("date_format"); + } else if (sqlObj instanceof SQLIntegerExpr) { + children.remove(i); + } + } + } + + @Override + public void nvl(SQLMethodInvokeExpr expr) { + expr.setMethodName("ifnull"); + } + + @Override + public void nvl2(SQLMethodInvokeExpr expr) { + expr.setMethodName("if"); + } + + @Override + public void length(SQLMethodInvokeExpr expr) { + expr.setMethodName("char_length"); + } + + @Override + public void instr(SQLMethodInvokeExpr expr) { + List children = expr.getChildren(); + expr.setMethodName("locate"); + if (children.size() >= 2) { + SQLObject sqlObject = children.get(0); + children.set(0, children.get(1)); + children.set(1, sqlObject); + } + } + + @Override + public void substr(SQLMethodInvokeExpr expr) { + List children = expr.getChildren(); + expr.setMethodName("substr"); + if (children.size() >= 2) { + SQLObject sqlObject = children.get(1); + if (sqlObject instanceof SQLNumberExpr) { + int val = ((SQLNumberExpr) sqlObject).getNumber().intValue(); + if (val <= 0) { + ((SQLNumberExpr) sqlObject).setNumber(1); + } + } else if (sqlObject instanceof SQLIntegerExpr) { + int val = ((SQLIntegerExpr) sqlObject).getNumber().intValue(); + if (val <= 0) { + ((SQLIntegerExpr) sqlObject).setNumber(1); + } + } + } + } + + @Override + public void add_months(SQLMethodInvokeExpr expr) { + List children = expr.getChildren(); + expr.setMethodName("date_add"); + SQLIdentifierExpr sqlCharExpr = null; + for (int i = 0, listSize = children.size(); i < listSize; i++) { + SQLObject sqlObj = children.get(i); + if (sqlObj instanceof SQLIntegerExpr) { + String num = ((SQLIntegerExpr) sqlObj).getNumber().toString(); + sqlCharExpr = new SQLIdentifierExpr(); + sqlCharExpr.setName(String.format("interval %s month", num)); + sqlCharExpr.setParent(sqlCharExpr); + children.set(i, sqlCharExpr); + } + } + } + + @Override + public void hextoraw(SQLMethodInvokeExpr expr) { + expr.setMethodName("UNHEX"); + } + + @Override + public void decode(SQLMethodInvokeExpr expr) { + SQLExpr expr1 = OracleSQLDataTypeTransformUtil.transformDecode(expr); + if (expr.getParent() instanceof SQLSelectItem) { + ((SQLSelectItem) expr.getParent()).setExpr(expr1); + } else if (expr.getParent() instanceof SQLBinaryOpExpr) { + ((SQLBinaryOpExpr) expr.getParent()).setRight(expr1); + } + } + + @Override + public String formatDate(String format) { + if(format.toUpperCase().startsWith("S")) { + format = format.substring(1); + } + return format.toUpperCase() + .replaceAll("YYYY", "%Y") + .replaceAll("MM", "%m") + .replaceAll("DD", "%d") + .replaceAll("HH24", "%H") + .replaceAll("MI", "%i") + .replaceAll("SS", "%s") + .replaceAll(".FF", ".%f") + .replaceAll(":FF", ".%f"); + } + + @Override + public String formatKeyField(String format) { + if (MySqlUtil.containsKeyWords(format)) { + return String.format("`%s`", format.toUpperCase()); + } + return format; + } + + @Override + public void alias(SQLExpr sqlExpr) { + if (sqlExpr.getParent() instanceof SQLSelectItem) {//取别名 as + String alias = ((SQLSelectItem) sqlExpr.getParent()).getAlias(); + if (!StringUtils.isEmpty(alias)) { + if (alias.lastIndexOf("\"") <= 0) { + ((SQLSelectItem) sqlExpr.getParent()).setAlias(formatKeyField(alias.toUpperCase())); + } + } + } + } + + /** + * 获取四舍五入小数点多少位 + * to_char(999995.6, '9999999999990.999999') + * @author rayson.fang + * @date 2021/02/05 13:36 + * @param str + * @return java.lang.Integer + */ + private Integer getRound(String str) { + int round = 0; + if (str.indexOf(".") > 0) { + round = str.split("\\.")[1].length(); + } + return round; + } + + + + +} diff --git a/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/util/SqlStringUtil.java b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/util/SqlStringUtil.java new file mode 100644 index 0000000..3ca4bfa --- /dev/null +++ b/src/main/java/com/raysonfang/sqltranslator/sql/dialect/starrocks/util/SqlStringUtil.java @@ -0,0 +1,73 @@ +package com.raysonfang.sqltranslator.sql.dialect.starrocks.util; + +import java.text.MessageFormat; +import java.util.ArrayList; + +/** + * Date: 2025/1/9 + * Author: rockyyin + * Description: 针对sql字符串转换的处理工具类 + */ +public class SqlStringUtil { + + // 这边SR都是用默认的桶数量来处理 + public static String primaryKey = "PRIMARY KEY ({0})\n" + + "DISTRIBUTED BY HASH ({1})"; + /** + * 处理createSQL 主要是将sql中的主键和其他索引截取,改造为SR的语法 + * @param createSql + * @return + */ + public static ArrayList suffixCreateSqlInterception(String createSql, String specialKey) { + //将create sql中CONSTRAINT关键字阶截断,第一部分直接返回,第二部分需要拼接SR的主键表的语法 + ArrayList CreateSqlPreAndSuffix = new ArrayList<>(); + if (specialKey == null) { + return null; + } else { + int index = createSql.indexOf(specialKey); + String intercepted = createSql.substring(0, index); + int lastIndexOf = intercepted.lastIndexOf(","); + StringBuilder sb = new StringBuilder(intercepted); + sb.deleteCharAt(lastIndexOf); + CreateSqlPreAndSuffix.add(sb.toString() + "\r)\n"); + String suffixStr = createSql.substring(index); + //先从 PRIMARY key 关键字获取列名称 + int primary_key = suffixStr.indexOf("PRIMARY KEY"); + String PKAndUk = suffixStr.substring(primary_key); + int leftParenthesis = PKAndUk.indexOf("("); + int rightParenthesis = PKAndUk.indexOf(")"); + String PkeyColumn = PKAndUk.substring(leftParenthesis+1, rightParenthesis); + //先从 unique关键字获取列名称 + int unique = suffixStr.indexOf("UNIQUE"); + String ukStr = suffixStr.substring(unique); + int uniqueKeyLeft = ukStr.indexOf("("); + int uniqueKeyRight = ukStr.indexOf(")"); + String UKeyColumn = ukStr.substring(uniqueKeyLeft+1, uniqueKeyRight); + String formatPK = MessageFormat.format(primaryKey, PkeyColumn+","+UKeyColumn, PkeyColumn+","+UKeyColumn); + + CreateSqlPreAndSuffix.add(formatPK); + return CreateSqlPreAndSuffix; + } + } + + /** + * 转换 insertAll 的语法 单个insert语句 + * -- 多条插入 + * INSERT ALL + * INTO employees (employee_id, first_name, last_name) VALUES (2, 'Jane', 'Smith') + * INTO employees (employee_id, first_name, last_name) VALUES (3, 'Bob', 'Johnson') + * SELECT * FROM dual; + * 需要转换到单条insert + * @param insertSql + * @return + */ + public static String insertAllToSingleInsert(String insertSql) { + + return ""; + } + + + + + +} diff --git a/src/test/java/com/raysonfang/sqltranslator/sql/OracleToMysqlTest.java b/src/test/java/com/raysonfang/sqltranslator/sql/OracleToMysqlTest.java index 25375a7..43635b4 100644 --- a/src/test/java/com/raysonfang/sqltranslator/sql/OracleToMysqlTest.java +++ b/src/test/java/com/raysonfang/sqltranslator/sql/OracleToMysqlTest.java @@ -27,7 +27,7 @@ public void test1() { " KEY_NAME VARCHAR2(500) ,\n" + " KEY_ENAME VARCHAR2(2000) ,\n" + " OR_VALID_D VARCHAR2(1) DEFAULT 0 ,\n" + - " MEMO VARCHAR2(2000) ,\n" + + " MEMO BLOB ,\n" + " TIME_MARK TIMESTAMP(6) ,\n" + " STA VARCHAR2(10) DEFAULT 1 ,\n" + " KEY_SEQ NUMBER(20) ,\n" + @@ -38,7 +38,17 @@ public void test1() { " FDELETE_ID VARCHAR2(1) DEFAULT 0 NOT NULL ,\n" + " constraint PK_T_DICT primary key(ID)\n" + ");"; - List stmtList = SQLUtils.parseStatements(sql, DbType.oracle); + String sql1 = "CREATE TABLE my_table (\n" + + " ID NUMBER(10),\n" + + " clob_column CLOB,\n" + + " blob_column BLOB,\n" + + " some_other_column VARCHAR2(100),\n" + + " creation_date DATE DEFAULT SYSDATE,\n" + + " last_modified DATE,\n" + + " CONSTRAINT pk_my_table PRIMARY KEY (ID),\n" + + " CONSTRAINT uk_my_table UNIQUE (some_other_column)\n" + + ")"; + List stmtList = SQLUtils.parseStatements(sql1, DbType.oracle); StringBuilder out = new StringBuilder(); OracleToMySqlOutputVisitor visitor = new OracleToMySqlOutputVisitor(out, false); for(SQLStatement sqlStatement : stmtList) { @@ -48,11 +58,12 @@ public void test1() { } public void test2_select() { + String sql = "SELECT '='||TRIM(' HELLO ')||'=' FROM DUAL;"; sql +="select sys_guid(),sysdate,'oracle' from dual;"; sql += "select key,value,to_char(sysdate, 'yyyyMMdd'),to_char(#{num})--测试\n" + " ,to_char(UPDATE_TIME, 'yyyyMMdd'),to_char(12312),to_char(state),to_char(#{num}) val,to_char(1123.26723,'99999999.99'),to_char( to_number( '4' ) + 1, '00' )\n" + - " from SYS_TEST where id = #{id}"; + " from SYS_TEST where id = #{id} and dt = to_date(#{dt}, 'yyyymmdd')"; List stmtList = SQLUtils.parseStatements(sql, DbType.oracle); StringBuilder out = new StringBuilder(); OracleToMySqlOutputVisitor visitor = new OracleToMySqlOutputVisitor(out, false); diff --git a/src/test/java/com/raysonfang/sqltranslator/sql/OracleToSRsqlTest.java b/src/test/java/com/raysonfang/sqltranslator/sql/OracleToSRsqlTest.java new file mode 100644 index 0000000..d3432e6 --- /dev/null +++ b/src/test/java/com/raysonfang/sqltranslator/sql/OracleToSRsqlTest.java @@ -0,0 +1,59 @@ +package com.raysonfang.sqltranslator.sql; + +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.raysonfang.sqltranslator.sql.dialect.starrocks.OracleToStarrocksOutputVisitor; +import com.raysonfang.sqltranslator.sql.dialect.starrocks.util.SqlStringUtil; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Date: 2025/1/8 + * Author: rockyyin + * Description: 测试create table 语法测试 + * + */ +public class OracleToSRsqlTest extends TestCase { + public void testCreateSQL() { + String sql1 = "CREATE TABLE my_table (\n" + + " ID NUMBER(10),\n" + + " clob_column CLOB,\n" + + " blob_column BLOB,\n" + + " some_other_column VARCHAR2(100),\n" + + " creation_date DATE DEFAULT SYSDATE,\n" + + " last_modified DATE,\n" + + " CONSTRAINT pk_my_table PRIMARY KEY (ID),\n" + + " CONSTRAINT uk_my_table UNIQUE (some_other_column)\n" + + ")"; + + String sql2 = "CREATE TABLE my_table (\n" + + " ID NUMBER(10),\n" + + " clob_column CLOB,\n" + + " blob_column BLOB,\n" + + " some_other_column VARCHAR2(100),\n" + + " creation_date DATE DEFAULT SYSDATE,\n" + + " last_modified DATE,\n" + + " CONSTRAINT pk_my_table PRIMARY KEY (ID),\n" + + " CONSTRAINT fk_department FOREIGN KEY (department_id) REFERENCES departments(department_id), \n" + + " CONSTRAINT uk_my_table UNIQUE (some_other_column)\n" + + ")"; + ArrayList preAndSuffix = SqlStringUtil.suffixCreateSqlInterception(sql2, "CONSTRAINT"); + System.out.println(preAndSuffix.get(0)); + +// CONSTRAINT +// String createSqlNoKey = SqlStringUtil.suffixCreateSqlInterception(sql1, "CONSTRAINT"); +// System.out.println(createSqlNoKey); + List stmtList = SQLUtils.parseStatements(preAndSuffix.get(0), DbType.oracle); + StringBuilder out = new StringBuilder(); + OracleToStarrocksOutputVisitor visitor = new OracleToStarrocksOutputVisitor(out, false); + for(SQLStatement sqlStatement : stmtList) { + sqlStatement.accept(visitor); + } + + System.out.println(out.toString() + "\n" +preAndSuffix.get(1)); + } + +}