diff --git a/src/main/java/com/alibaba/druid/DbType.java b/src/main/java/com/alibaba/druid/DbType.java index 629b44ed9e..4544237e06 100644 --- a/src/main/java/com/alibaba/druid/DbType.java +++ b/src/main/java/com/alibaba/druid/DbType.java @@ -99,4 +99,8 @@ public enum DbType { return null; } } + + public final boolean equals(String other) { + return this == of(other); + } } diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 1a043c8eb0..74622ad7f6 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -867,7 +867,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements } public void setValidationQueryTimeout(int validationQueryTimeout) { - if (validationQueryTimeout < 0 && JdbcConstants.SQL_SERVER.equals(dbTypeName)) { + if (validationQueryTimeout < 0 && DbType.of(dbTypeName) == DbType.sqlserver) { LOG.error("validationQueryTimeout should be >= 0"); } this.validationQueryTimeout = validationQueryTimeout; diff --git a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java index b754af1d13..9e049f6eda 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java +++ b/src/main/java/com/alibaba/druid/pool/DruidConnectionHolder.java @@ -29,6 +29,7 @@ import java.util.concurrent.locks.ReentrantLock; import javax.sql.ConnectionEventListener; import javax.sql.StatementEventListener; +import com.alibaba.druid.DbType; import com.alibaba.druid.pool.DruidAbstractDataSource.PhysicalConnectionInfo; import com.alibaba.druid.proxy.jdbc.WrapperProxy; import com.alibaba.druid.support.logging.Log; @@ -112,10 +113,11 @@ public final class DruidConnectionHolder { { boolean initUnderlyHoldability = !holdabilityUnsupported; - if (JdbcConstants.SYBASE.equals(dataSource.dbTypeName) // - || JdbcConstants.DB2.equals(dataSource.dbTypeName) // - || JdbcConstants.HIVE.equals(dataSource.dbTypeName) // - || JdbcConstants.ODPS.equals(dataSource.dbTypeName) // + DbType dbType = DbType.of(dataSource.dbTypeName); + if (dbType == DbType.sybase // + || dbType == DbType.db2 // + || dbType == DbType.hive // + || dbType == DbType.odps // ) { initUnderlyHoldability = false; } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlObjectImpl.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlObjectImpl.java index c4ddb32161..bbbbd815a5 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlObjectImpl.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/ast/MySqlObjectImpl.java @@ -22,13 +22,13 @@ import com.alibaba.druid.sql.visitor.SQLASTVisitor; public abstract class MySqlObjectImpl extends SQLObjectImpl implements MySqlObject { @Override - protected void accept0(SQLASTVisitor visitor) { - if (visitor instanceof MySqlASTVisitor) { - accept0((MySqlASTVisitor) visitor); + protected void accept0(SQLASTVisitor v) { + if (v instanceof MySqlASTVisitor) { + accept0((MySqlASTVisitor) v); } else { - throw new IllegalArgumentException("not support visitor type : " + visitor.getClass().getName()); + throw new IllegalArgumentException("not support visitor type : " + v.getClass().getName()); } } - public abstract void accept0(MySqlASTVisitor visitor); + public abstract void accept0(MySqlASTVisitor v); } diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java index 15da621892..db0e29153e 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/parser/MySqlExprParser.java @@ -24,10 +24,7 @@ import com.alibaba.druid.sql.ast.statement.SQLForeignKeyImpl.Option; 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.MysqlForeignKey; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlUserName; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.*; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; import com.alibaba.druid.sql.parser.*; import com.alibaba.druid.util.FnvHash; @@ -208,6 +205,34 @@ public class MySqlExprParser extends SQLExprParser { currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.LOCALTIME); } else if(hash_lower == FnvHash.Constants.LOCALTIMESTAMP && !quoteStart) { currentTimeExpr = new SQLCurrentTimeExpr(SQLCurrentTimeExpr.Type.LOCALTIMESTAMP); + } else if (hash_lower == FnvHash.Constants.JSON_TABLE) { + if (lexer.identifierEquals("JSON_TABLE")) { + lexer.nextToken(); + accept(Token.LPAREN); + + MySqlJSONTableExpr jsonTable = new MySqlJSONTableExpr(); + jsonTable.setExpr( + this.expr()); + accept(Token.COMMA); + jsonTable.setPath( + this.expr()); + acceptIdentifier("COLUMNS"); + accept(Token.LPAREN); + for (;lexer.token() != Token.RPAREN;) { + jsonTable.addColumn( + parseJsonTableColumn()); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + accept(Token.RPAREN); + + accept(Token.RPAREN); + return jsonTable; + } } else if((hash_lower == FnvHash.Constants._LATIN1) && !quoteStart) { lexer.nextToken(); @@ -505,6 +530,77 @@ public class MySqlExprParser extends SQLExprParser { } + protected MySqlJSONTableExpr.Column parseJsonTableColumn() { + MySqlJSONTableExpr.Column column = new MySqlJSONTableExpr.Column(); + + SQLName name = this.name(); + column.setName( + name); + + if (lexer.token() == Token.FOR) { + lexer.nextToken(); + acceptIdentifier("ORDINALITY"); + } else { + boolean nested = name instanceof SQLIdentifierExpr + && name.nameHashCode64() == FnvHash.Constants.NESTED; + + if (!nested) { + column.setDataType( + this.parseDataType()); + } + + if (lexer.token() == Token.EXISTS) { + lexer.nextToken(); + column.setExists(true); + } + + if (lexer.identifierEquals(FnvHash.Constants.PATH)) { + lexer.nextToken(); + column.setPath( + this.primary()); + } + + if (name instanceof SQLIdentifierExpr + && name.nameHashCode64() == FnvHash.Constants.NESTED) { + acceptIdentifier("COLUMNS"); + accept(Token.LPAREN); + for (;lexer.token() != Token.RPAREN;) { + MySqlJSONTableExpr.Column nestedColumn = parseJsonTableColumn(); + column.addNestedColumn(nestedColumn); + + if (lexer.token() == Token.COMMA) { + lexer.nextToken(); + continue; + } + break; + } + accept(Token.RPAREN); + } + + for (int i = 0; i < 2; ++i) { + if (lexer.identifierEquals("ERROR") + || lexer.token() == Token.DEFAULT + || lexer.token() == Token.NULL) { + if (lexer.token() == Token.DEFAULT) { + lexer.nextToken(); + } + + SQLExpr expr = this.expr(); + accept(Token.ON); + if (lexer.identifierEquals("ERROR")) { + lexer.nextToken(); + column.setOnError(expr); + } else { + acceptIdentifier("EMPTY"); + column.setOnEmpty(expr); + } + } + } + } + + return column; + } + public final SQLExpr primaryRest(SQLExpr expr) { if (expr == null) { throw new IllegalArgumentException("expr"); diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java index 8b82f79efb..aa128a231f 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlASTVisitor.java @@ -22,10 +22,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock; import com.alibaba.druid.sql.dialect.mysql.ast.*; import com.alibaba.druid.sql.dialect.mysql.ast.clause.*; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlUserName; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.*; import com.alibaba.druid.sql.dialect.mysql.ast.statement.*; import com.alibaba.druid.sql.visitor.SQLASTVisitor; @@ -1430,5 +1427,20 @@ public interface MySqlASTVisitor extends SQLASTVisitor { } + default boolean visit(MySqlJSONTableExpr x) { + return true; + } + default void endVisit(MySqlJSONTableExpr x) { + + } + + + default boolean visit(MySqlJSONTableExpr.Column x) { + return true; + } + + default void endVisit(MySqlJSONTableExpr.Column x) { + + } } // diff --git a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java index 43b69df6cb..fba5a274f6 100644 --- a/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/dialect/mysql/visitor/MySqlOutputVisitor.java @@ -23,10 +23,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.*; import com.alibaba.druid.sql.dialect.mysql.ast.clause.*; import com.alibaba.druid.sql.dialect.mysql.ast.clause.ConditionValue.ConditionType; import com.alibaba.druid.sql.dialect.mysql.ast.clause.MySqlCaseStatement.MySqlWhenStatement; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlCharExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOutFileExpr; -import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlUserName; +import com.alibaba.druid.sql.dialect.mysql.ast.expr.*; import com.alibaba.druid.sql.dialect.mysql.ast.statement.*; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement.TableSpaceOption; import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateUserStatement.UserSpecification; @@ -5394,6 +5391,79 @@ public class MySqlOutputVisitor extends SQLASTOutputVisitor implements MySqlASTV return hiveVisit(x); } + public boolean visit(MySqlJSONTableExpr x) { + print0(ucase ? "JSON_TABLE(" : "json_table("); + x.getExpr().accept(this); + print(' '); + x.getPath().accept(this); + incrementIndent(); + println(); + print0(ucase ? "COLUMNS (" : "columns ("); + incrementIndent(); + println(); + printlnAndAccept(x.getColumns(), ", "); + decrementIndent(); + println(); + print(')'); + decrementIndent(); + println(); + print(')'); + + return false; + } + + public boolean visit(MySqlJSONTableExpr.Column x) { + x.getName().accept(this); + + if (x.isOrdinality()) { + print0(ucase ? " FOR ORDINALITY" : " for ordinality"); + } + + SQLDataType dataType = x.getDataType(); + if (dataType != null) { + print(' '); + dataType.accept(this); + } + + if (x.isExists()) { + print0(ucase ? " EXISTS" : " exists"); + } + + SQLExpr path = x.getPath(); + if (path != null) { + print0(ucase ? " PATH " : " path "); + path.accept(this); + } + + List nestedColumns = x.getNestedColumns(); + if (nestedColumns.size() > 0) { + print0(ucase ? " COLUMNS (" : " columns ("); + printAndAccept(nestedColumns, ", "); + print(')'); + } + + SQLExpr onEmpty = x.getOnEmpty(); + if (onEmpty != null) { + print(' '); + if (!(onEmpty instanceof SQLNullExpr || onEmpty instanceof SQLIdentifierExpr)) { + print0(ucase ? "DEFAULT " : "default "); + } + onEmpty.accept(this); + print0(ucase ? " ON EMPTY" : " on empty"); + } + + SQLExpr onError = x.getOnError(); + if (onError != null) { + print(' '); + if (!(onEmpty instanceof SQLNullExpr || onEmpty instanceof SQLIdentifierExpr)) { + print0(ucase ? "DEFAULT " : "default "); + } + onError.accept(this); + print0(ucase ? " ON ERROR" : " on error"); + } + return false; + } + // public boolean visit(SQLSelectItem x) { // SQLExpr expr = x.getExpr(); // diff --git a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java index 3f291f5e59..cd46c4992e 100644 --- a/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java +++ b/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java @@ -3008,27 +3008,27 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet } if (param instanceof InputStream) { - print0("'"); + print0("''"); return; } if (param instanceof Reader) { - print0("'"); + print0("''"); return; } if (param instanceof Blob) { - print0("'"); + print0("''"); return; } if (param instanceof NClob) { - print0("'"); + print0("''"); return; } if (param instanceof Clob) { - print0("'"); + print0("''"); return; } diff --git a/src/main/java/com/alibaba/druid/support/profile/Profiler.java b/src/main/java/com/alibaba/druid/support/profile/Profiler.java index ddd7d6cbd2..38129331d3 100644 --- a/src/main/java/com/alibaba/druid/support/profile/Profiler.java +++ b/src/main/java/com/alibaba/druid/support/profile/Profiler.java @@ -55,6 +55,10 @@ public class Profiler { public static void release(long nanos) { ProfileEntry current = currentLocal.get(); + if (current == null) { + return; + } + currentLocal.set(current.getParent()); ProfileEntryReqStat stat = null; diff --git a/src/main/java/com/alibaba/druid/util/FnvHash.java b/src/main/java/com/alibaba/druid/util/FnvHash.java index 05e21576f7..3583754372 100644 --- a/src/main/java/com/alibaba/druid/util/FnvHash.java +++ b/src/main/java/com/alibaba/druid/util/FnvHash.java @@ -867,6 +867,7 @@ public final class FnvHash { long FACE_FEATURE_EXTRACT_V1 = fnv1a_64_lower("FACE_FEATURE_EXTRACT_V1"); long TEXT_FEATURE_EXTRACT_V1 = fnv1a_64_lower("TEXT_FEATURE_EXTRACT_V1"); + long JSON_TABLE = fnv1a_64_lower("JSON_TABLE"); long JSON_EXTRACT = fnv1a_64_lower("JSON_EXTRACT"); long JSON_EXTRACT_SCALAR = fnv1a_64_lower("json_extract_scalar"); long JSON_ARRAY_GET = fnv1a_64_lower("JSON_ARRAY_GET"); @@ -1112,5 +1113,6 @@ public final class FnvHash { long _UJIS = fnv1a_64_lower("_UJIS"); long X = fnv1a_64_lower("X"); long TRANSFORM = fnv1a_64_lower("TRANSFORM"); + long NESTED = fnv1a_64_lower("NESTED"); } } diff --git a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java index 97413f6b82..18c6f766d7 100644 --- a/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java +++ b/src/test/java/com/alibaba/druid/bvt/sql/PagerUtilsTest_Count_PG_0.java @@ -30,6 +30,20 @@ public class PagerUtilsTest_Count_PG_0 extends TestCase { "FROM t", result); } + public void test_pg_3() throws Exception { + String sql = "select * from test where shape.STIntersects(geometry::STGeomFromText('POLYGON ((86610.054 86610.054,112372.95799999963 88785.5940000005,112372.91199999955 88675.996999999508,86610.054 86610.054))',0))=1;"; + String countSql = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); + assertEquals("SELECT COUNT(*)\n" + + "FROM test\n" + + "WHERE shape.STIntersects(geometry::STGeomFromText('POLYGON ((86610.054 86610.054,112372.95799999963 88785.5940000005,112372.91199999955 88675.996999999508,86610.054 86610.054))', 0)) = 1", countSql); + + String limitSql = PagerUtils.limit(sql, JdbcConstants.POSTGRESQL, 100, 10); + assertEquals("SELECT *\n" + + "FROM test\n" + + "WHERE shape.STIntersects(geometry::STGeomFromText('POLYGON ((86610.054 86610.054,112372.95799999963 88785.5940000005,112372.91199999955 88675.996999999508,86610.054 86610.054))', 0)) = 1\n" + + "LIMIT 10 OFFSET 100", limitSql); + } + public void test_pg_group_0() throws Exception { String sql = "select type, count(*) from t group by type"; String result = PagerUtils.count(sql, JdbcConstants.POSTGRESQL); @@ -53,4 +67,5 @@ public class PagerUtilsTest_Count_PG_0 extends TestCase { "\tFROM t2\n" + ") ALIAS_COUNT", result); } + }