From 40c8f56cdeaa739bb924a3e09a148ce55a34699a Mon Sep 17 00:00:00 2001 From: zhouhao Date: Wed, 15 Apr 2026 20:52:37 +0800 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20=E5=8D=87=E7=BA=A7=20JSqlParser?= =?UTF-8?q?=205.3=20=E5=B9=B6=E8=A1=A5=E5=85=85=E5=85=BC=E5=AE=B9=E8=A6=86?= =?UTF-8?q?=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 12 +- .../jetlinks/reactor/ql/DefaultReactorQL.java | 89 ++-- .../reactor/ql/feature/FromFeature.java | 72 ++- .../reactor/ql/feature/ValueMapFeature.java | 15 +- .../ql/supports/DefaultReactorQLMetadata.java | 47 +- .../ql/supports/ExpressionVisitorAdapter.java | 426 +++++++++--------- .../supports/agg/CollectListAggFeature.java | 6 +- .../supports/agg/CollectRowAggMapFeature.java | 5 +- .../ql/supports/agg/MapAggFeature.java | 2 +- .../distinct/DefaultDistinctFeature.java | 42 +- .../ql/supports/filter/IfValueMapFeature.java | 3 +- .../reactor/ql/supports/filter/InFilter.java | 24 +- .../ql/supports/from/FromValuesFeature.java | 78 ++-- .../supports/from/SubSelectFromFeature.java | 13 +- .../ql/supports/group/GroupByTakeFeature.java | 2 +- .../reactor/ql/supports/map/CastFeature.java | 2 +- .../ql/supports/map/DateFormatFeature.java | 3 +- .../ql/supports/map/FunctionMapFeature.java | 3 +- .../ql/supports/map/PropertyMapFeature.java | 58 ++- .../ql/supports/map/SelectFeature.java | 4 +- .../SingleParameterFunctionMapFeature.java | 3 +- .../reactor/ql/utils/ExpressionUtils.java | 4 +- .../reactor/ql/DefaultContextRecordTest.java | 123 +++++ .../jetlinks/reactor/ql/ReactorQLTest.java | 46 +- .../ql/examples/GroupByWindowTest.java | 7 +- .../ql/supports/FeatureCoverageTest.java | 353 +++++++++++++++ .../reactor/ql/utils/ExpressionUtilsTest.java | 6 +- 27 files changed, 986 insertions(+), 462 deletions(-) create mode 100644 src/test/java/org/jetlinks/reactor/ql/DefaultContextRecordTest.java create mode 100644 src/test/java/org/jetlinks/reactor/ql/supports/FeatureCoverageTest.java diff --git a/pom.xml b/pom.xml index 1f935ab..16f5082 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ UTF-8 zh_CN - 1.8 + 11 ${java.version} 2020.0.38 @@ -205,6 +205,7 @@ **/ExpressionVisitorAdapter* **/ReactorQLMetadata* + net/sf/jsqlparser/** @@ -253,10 +254,9 @@ org.apache.maven.plugins maven-compiler-plugin - 3.1 + 3.11.0 - ${project.build.jdk} - ${project.build.jdk} + ${project.build.jdk} ${project.build.sourceEncoding} @@ -288,7 +288,7 @@ com.github.jsqlparser jsqlparser - 4.6 + 5.3 @@ -397,4 +397,4 @@ https://maven.aliyun.com/nexus/content/groups/public/ - \ No newline at end of file + diff --git a/src/main/java/org/jetlinks/reactor/ql/DefaultReactorQL.java b/src/main/java/org/jetlinks/reactor/ql/DefaultReactorQL.java index 60d82b8..8ed6e8d 100644 --- a/src/main/java/org/jetlinks/reactor/ql/DefaultReactorQL.java +++ b/src/main/java/org/jetlinks/reactor/ql/DefaultReactorQL.java @@ -185,12 +185,12 @@ protected Function, Flux> createJoin() { Function> rightStreamGetter = null; //join (select deviceId,avg(temp) from temp group by interval('10s'),deviceId ) - if (from instanceof SubSelect) { + if (from instanceof ParenthesedSelect) { String alias = from.getAlias() == null ? null : from.getAlias().getName(); //子查询 DefaultReactorQL ql = new DefaultReactorQL(new DefaultReactorQLMetadata(metadata, - ((PlainSelect) ((SubSelect) from).getSelectBody()))); + ((PlainSelect) ((ParenthesedSelect) from).getSelect().getPlainSelect()))); rightStreamGetter = record -> ql .builder @@ -301,7 +301,8 @@ protected Function, Flux> createGroupBy() groupByRef.set(nameMapper); } }; - for (Expression groupByExpression : groupBy.getGroupByExpressionList().getExpressions()) { + for (Object expression : groupBy.getGroupByExpressionList().getExpressions()) { + Expression groupByExpression = (Expression) expression; //函数分组, group by interval('1s') if (groupByExpression instanceof net.sf.jsqlparser.expression.Function) { featureConsumer.accept(null, @@ -404,54 +405,42 @@ private Function, Flux> createMapper() { List> allMapper = new ArrayList<>(); - for (SelectItem selectItem : metadata.getSql().getSelectItems()) { - selectItem.accept(new SelectItemVisitorAdapter() { - // select a,b,c - @Override - public void visit(SelectExpressionItem item) { - Expression expression = item.getExpression(); - String alias = item.getAlias() == null ? expression.toString() : item.getAlias().getName(); - String fAlias = SqlUtils.getCleanStr(alias); - // select a,b,c - createExpressionMapper(expression).ifPresent(mapper -> mappers.put(fAlias, mapper)); - // select count(),max(val)... - createAggMapper(expression).ifPresent(mapper -> aggMapper.put(fAlias, mapper)); - //flatMap - ValueFlatMapFeature.createMapperByExpression(expression, metadata) - .ifPresent(mapper -> flatMappers.put(fAlias, mapper)); - - if (!mappers.containsKey(fAlias) && !aggMapper.containsKey(fAlias) && !flatMappers.containsKey(fAlias)) { - throw new UnsupportedOperationException("Unsupported expression:" + expression); - } - } - - //select * - @Override - public void visit(AllColumns columns) { - allMapper.add(ReactorQLRecord::putRecordToResult); - } - - //select t.* - @Override - public void visit(AllTableColumns columns) { - String name; - Alias alias = columns.getTable().getAlias(); - if (alias == null) { - name = SqlUtils.getCleanStr(columns.getTable().getName()); - } else { - name = SqlUtils.getCleanStr(alias.getName()); - } - allMapper.add(record -> record - .getRecord(name) - .ifPresent(v -> { - if (v instanceof Map) { - record.setResults(((Map) v)); - } else { - record.setResult(name, v); - } - })); + for (SelectItem selectItem : metadata.getSql().getSelectItems()) { + Expression expression = selectItem.getExpression(); + if (expression instanceof AllColumns) { + allMapper.add(ReactorQLRecord::putRecordToResult); + continue; + } + if (expression instanceof AllTableColumns) { + AllTableColumns columns = (AllTableColumns) expression; + String name; + Alias alias = columns.getTable().getAlias(); + if (alias == null) { + name = SqlUtils.getCleanStr(columns.getTable().getName()); + } else { + name = SqlUtils.getCleanStr(alias.getName()); } - }); + allMapper.add(record -> record + .getRecord(name) + .ifPresent(v -> { + if (v instanceof Map) { + record.setResults(((Map) v)); + } else { + record.setResult(name, v); + } + })); + continue; + } + String alias = selectItem.getAlias() == null ? expression.toString() : selectItem.getAlias().getName(); + String fAlias = SqlUtils.getCleanStr(alias); + createExpressionMapper(expression).ifPresent(mapper -> mappers.put(fAlias, mapper)); + createAggMapper(expression).ifPresent(mapper -> aggMapper.put(fAlias, mapper)); + ValueFlatMapFeature.createMapperByExpression(expression, metadata) + .ifPresent(mapper -> flatMappers.put(fAlias, mapper)); + + if (!mappers.containsKey(fAlias) && !aggMapper.containsKey(fAlias) && !flatMappers.containsKey(fAlias)) { + throw new UnsupportedOperationException("Unsupported expression:" + expression); + } } Function> _resultMapper; diff --git a/src/main/java/org/jetlinks/reactor/ql/feature/FromFeature.java b/src/main/java/org/jetlinks/reactor/ql/feature/FromFeature.java index 41cb722..5ece3ad 100644 --- a/src/main/java/org/jetlinks/reactor/ql/feature/FromFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/feature/FromFeature.java @@ -40,56 +40,46 @@ static Function> createFromMapperByFrom( if (body == null) { return ctx -> ctx.getDataSource(null).map(val -> ReactorQLRecord.newRecord(null, val, ctx)); } - AtomicReference>> ref = new AtomicReference<>(); - - body.accept(new FromItemVisitorAdapter() { - // from table - @Override - public void visit(Table table) { - ref.set(metadata.getFeatureNow(FeatureId.From.table) - .createFromMapper(table, metadata)); - } - - // from (select ...) - @Override - public void visit(SubSelect subSelect) { - ref.set(metadata.getFeatureNow(FeatureId.From.subSelect) - .createFromMapper(subSelect, metadata)); - } - - // select * from (values(6)) t(v) - @Override - public void visit(ValuesList valuesList) { - ref.set(metadata.getFeatureNow(FeatureId.From.values) - .createFromMapper(valuesList, metadata)); - } - - //select * from mysql(...) - @Override - public void visit(TableFunction tableFunction) { - ref.set(metadata - .getFeatureNow(FeatureId.From.of(tableFunction.getFunction().getName()), - tableFunction::toString) - .createFromMapper(tableFunction, metadata)); - } - - @Override - public void visit(ParenthesisFromItem aThis) { - ref.set(createFromMapperByFrom(aThis.getFromItem(), metadata)); + if (body instanceof Table) { + return metadata.getFeatureNow(FeatureId.From.table) + .createFromMapper(body, metadata); + } + if (body instanceof ParenthesedSelect || body instanceof Select) { + return metadata.getFeatureNow(FeatureId.From.subSelect) + .createFromMapper(body, metadata); + } + if (body instanceof Values) { + return metadata.getFeatureNow(FeatureId.From.values) + .createFromMapper(body, metadata); + } + if (body instanceof ParenthesedFromItem) { + ParenthesedFromItem fromItem = (ParenthesedFromItem) body; + if (fromItem.getFromItem() instanceof Values) { + return metadata.getFeatureNow(FeatureId.From.values) + .createFromMapper(body, metadata); } - }); - if (ref.get() == null) { - throw new UnsupportedOperationException("不支持的查询:" + body); + return createFromMapperByFrom(fromItem.getFromItem(), metadata); + } + if (body instanceof TableFunction) { + TableFunction tableFunction = (TableFunction) body; + return metadata + .getFeatureNow(FeatureId.From.of(tableFunction.getFunction().getName()), + tableFunction::toString) + .createFromMapper(tableFunction, metadata); } - return ref.get(); + throw new UnsupportedOperationException("不支持的查询:" + body); } - static Function> createFromMapperByBody(SelectBody body, ReactorQLMetadata metadata) { + static Function> createFromMapperByBody(Select body, ReactorQLMetadata metadata) { FromItem from = null; if (body instanceof PlainSelect) { PlainSelect select = ((PlainSelect) body); from = select.getFromItem(); + } else if (body instanceof ParenthesedSelect) { + return createFromMapperByBody(((ParenthesedSelect) body).getSelect(), metadata); + } else if (body instanceof Values) { + from = body; } return createFromMapperByFrom(from, metadata); } diff --git a/src/main/java/org/jetlinks/reactor/ql/feature/ValueMapFeature.java b/src/main/java/org/jetlinks/reactor/ql/feature/ValueMapFeature.java index b7414d1..fe5e3fb 100644 --- a/src/main/java/org/jetlinks/reactor/ql/feature/ValueMapFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/feature/ValueMapFeature.java @@ -17,8 +17,9 @@ import net.sf.jsqlparser.expression.*; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.statement.select.SubSelect; +import net.sf.jsqlparser.statement.select.Select; import org.apache.commons.collections.CollectionUtils; import org.jetlinks.reactor.ql.ReactorQLMetadata; import org.jetlinks.reactor.ql.ReactorQLRecord; @@ -77,7 +78,7 @@ public void visit(net.sf.jsqlparser.expression.Function function) { //select (select * from xxx) data1 from ... @Override - public void visit(SubSelect subSelect) { + public void visit(Select subSelect) { ref.set(metadata .getFeatureNow(FeatureId.ValueMap.select, expr::toString) .createMapper(subSelect, metadata)); @@ -190,6 +191,12 @@ public void visit(DoubleValue value) { ref.set((v) -> val); } + @Override + public void visit(BooleanValue value) { + Mono val = Mono.just(value.getValue()); + ref.set((v) -> val); + } + //select {d 'yyyy-mm-dd'} @Override public void visit(DateValue value) { @@ -291,7 +298,7 @@ static Tuple2>, Function expressions; //只能有2个参数 if (function.getParameters() == null - || CollectionUtils.isEmpty(expressions = function.getParameters().getExpressions()) + || CollectionUtils.isEmpty(expressions = ExpressionUtils.getFunctionParameter(function)) || expressions.size() != 2) { throw new IllegalArgumentException("The number of parameters must be 2 :" + expression); } @@ -301,6 +308,8 @@ static Tuple2>, Function) expression).size() == 1) { + return createBinaryMapper(((ExpressionList) expression).get(0), metadata); } else { throw new UnsupportedOperationException("Unsupported expression:" + expression); } diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/DefaultReactorQLMetadata.java b/src/main/java/org/jetlinks/reactor/ql/supports/DefaultReactorQLMetadata.java index 8ff4c01..6d45d7a 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/DefaultReactorQLMetadata.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/DefaultReactorQLMetadata.java @@ -18,6 +18,7 @@ import com.google.common.collect.Maps; import lombok.SneakyThrows; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import org.apache.commons.collections.CollectionUtils; @@ -313,23 +314,23 @@ static void createCalculator(BiFunction CastUtils - .handleFirst(stream, (first, flux) -> { - TreeSet set = - CastUtils.castCollection(first, new TreeSet<>(CompareUtils::compare)); - - return flux - .skip(1) - .concatMap(val -> Flux - .just(val) - .as(CastUtils::flatStream) - .map(_val -> handleContain(set, _val)) - // 如果是空数组,则contains_all结果为true - .defaultIfEmpty(true)) - // 参数为空,返回false - .defaultIfEmpty(false) - .all(Boolean::booleanValue); - } - ))); + .handleFirst(stream, (first, flux) -> { + TreeSet set = + CastUtils.castCollection(first, new TreeSet<>(CompareUtils::compare)); + + return flux + .skip(1) + .concatMap(val -> Flux + .just(val) + .as(CastUtils::flatStream) + .map(_val -> handleContain(set, _val)) + // 如果是空数组,则contains_all结果为true + .defaultIfEmpty(true)) + // 参数为空,返回false + .defaultIfEmpty(false) + .all(Boolean::booleanValue); + } + ))); //select not_contains(val,'a','b','c') addGlobal( @@ -619,7 +620,15 @@ private synchronized Map features() { @SneakyThrows public DefaultReactorQLMetadata(String sql) { - this.selectSql = ((PlainSelect) ((Select) CCJSqlParserUtil.parse(sql)).getSelectBody()); + Statement statement = CCJSqlParserUtil.parse(sql); + if (!(statement instanceof Select)) { + throw new UnsupportedOperationException("select support only"); + } + Select body = ((Select) statement).getSelectBody(); + if (!(body instanceof PlainSelect)) { + throw new UnsupportedOperationException("plain select support only : select * from ( .... )"); + } + this.selectSql = ((PlainSelect) body); init(); } @@ -697,6 +706,6 @@ public Collection getFeatures() { } private static boolean handleContain(Collection left, Object val) { - return CompareUtils.contains(left,val); + return CompareUtils.contains(left, val); } } diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/ExpressionVisitorAdapter.java b/src/main/java/org/jetlinks/reactor/ql/supports/ExpressionVisitorAdapter.java index 8c4a847..97017dd 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/ExpressionVisitorAdapter.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/ExpressionVisitorAdapter.java @@ -20,453 +20,433 @@ import net.sf.jsqlparser.expression.operators.arithmetic.*; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.conditional.OrExpression; -import net.sf.jsqlparser.expression.operators.conditional.XorExpression; import net.sf.jsqlparser.expression.operators.relational.*; import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.statement.select.AllColumns; -import net.sf.jsqlparser.statement.select.AllTableColumns; -import net.sf.jsqlparser.statement.select.SubSelect; +import net.sf.jsqlparser.statement.piped.FromQuery; +import net.sf.jsqlparser.statement.select.ParenthesedSelect; +import net.sf.jsqlparser.statement.select.Select; @Generated -public interface ExpressionVisitorAdapter extends ExpressionVisitor { - - default void visit(BinaryExpression expression) { +public abstract class ExpressionVisitorAdapter extends net.sf.jsqlparser.expression.ExpressionVisitorAdapter { + public void visit(BinaryExpression expression) { } - default void visit(ComparisonOperator expression) { - + public void visit(ComparisonOperator expression) { } - @Override - default void visit(BitwiseRightShift aThis) { - visit((BinaryExpression) aThis); + public void visit(NullValue nullValue) { } - @Override - default void visit(BitwiseLeftShift aThis) { - visit((BinaryExpression) aThis); + public void visit(Function function) { } - @Override - default void visit(NullValue nullValue) { - + public void visit(SignedExpression signedExpression) { } - @Override - default void visit(Function function) { + public void visit(JdbcParameter jdbcParameter) { } - @Override - default void visit(SignedExpression signedExpression) { + public void visit(JdbcNamedParameter jdbcNamedParameter) { } - @Override - default void visit(JdbcParameter jdbcParameter) { + public void visit(NumericBind numericBind) { } - @Override - default void visit(JdbcNamedParameter jdbcNamedParameter) { + public void visit(DoubleValue doubleValue) { } - @Override - default void visit(DoubleValue doubleValue) { - + public void visit(BooleanValue booleanValue) { } - @Override - default void visit(LongValue longValue) { + public void visit(LongValue longValue) { } - @Override - default void visit(HexValue hexValue) { + public void visit(HexValue hexValue) { } - @Override - default void visit(DateValue dateValue) { + public void visit(DateValue dateValue) { } - @Override - default void visit(TimeValue timeValue) { - } - - @Override - default void visit(TimestampValue timestampValue) { + public void visit(TimeValue timeValue) { } - @Override - default void visit(Parenthesis parenthesis) { - - } - - @Override - default void visit(StringValue stringValue) { - - } - - @Override - default void visit(Addition addition) { - visit((BinaryExpression) addition); - } - - @Override - default void visit(Division division) { - visit((BinaryExpression) division); - } - - @Override - default void visit(IntegerDivision division) { - visit((BinaryExpression) division); + public void visit(TimestampValue timestampValue) { } - @Override - default void visit(Multiplication multiplication) { - visit((BinaryExpression) multiplication); + public void visit(Parenthesis parenthesis) { } - @Override - default void visit(Subtraction subtraction) { - visit((BinaryExpression) subtraction); + public void visit(StringValue stringValue) { } - @Override - default void visit(AndExpression andExpression) { + public void visit(AndExpression andExpression) { visit((BinaryExpression) andExpression); } - @Override - default void visit(OrExpression orExpression) { + public void visit(OrExpression orExpression) { visit((BinaryExpression) orExpression); } - @Override - default void visit(Between between) { - + public void visit(Between between) { } - @Override - default void visit(EqualsTo equalsTo) { - visit((BinaryExpression) equalsTo); - visit((ComparisonOperator) equalsTo); + public void visit(InExpression inExpression) { } - @Override - default void visit(GreaterThan greaterThan) { - visit((BinaryExpression) greaterThan); - visit((ComparisonOperator) greaterThan); + public void visit(IsNullExpression isNullExpression) { } - @Override - default void visit(GreaterThanEquals greaterThanEquals) { - visit((BinaryExpression) greaterThanEquals); - visit((ComparisonOperator) greaterThanEquals); + public void visit(IsBooleanExpression isBooleanExpression) { } - @Override - default void visit(InExpression inExpression) { - + public void visit(Column tableColumn) { } - @Override - default void visit(FullTextSearch fullTextSearch) { - + public void visit(Select subSelect) { } - @Override - default void visit(IsNullExpression isNullExpression) { - + public void visit(CaseExpression caseExpression) { } - @Override - default void visit(IsBooleanExpression isBooleanExpression) { - + public void visit(ExistsExpression existsExpression) { } - @Override - default void visit(LikeExpression likeExpression) { - visit((BinaryExpression) likeExpression); + public void visit(NotExpression notExpression) { } - @Override - default void visit(MinorThan minorThan) { - visit((BinaryExpression) minorThan); - visit((ComparisonOperator) minorThan); + public void visit(CastExpression castExpression) { } - @Override - default void visit(MinorThanEquals minorThanEquals) { - visit((BinaryExpression) minorThanEquals); - visit((ComparisonOperator) minorThanEquals); + public void visit(ArrayExpression arrayExpression) { } - @Override - default void visit(NotEqualsTo notEqualsTo) { - visit((BinaryExpression) notEqualsTo); - visit((ComparisonOperator) notEqualsTo); + public void visit(IntervalExpression intervalExpression) { } - @Override - default void visit(Column tableColumn) { - + public void visit(UserVariable var) { } @Override - default void visit(SubSelect subSelect) { - + public Void visit(NullValue nullValue, S context) { + visit(nullValue); + return null; } @Override - default void visit(CaseExpression caseExpression) { + public Void visit(Function function, S context) { + visit(function); + return null; } @Override - default void visit(WhenClause whenClause) { - + public Void visit(SignedExpression signedExpression, S context) { + visit(signedExpression); + return null; } @Override - default void visit(ExistsExpression existsExpression) { - + public Void visit(JdbcParameter jdbcParameter, S context) { + visit(jdbcParameter); + return null; } - @Override - default void visit(AnyComparisonExpression anyComparisonExpression) { - + public Void visit(JdbcNamedParameter jdbcNamedParameter, S context) { + visit(jdbcNamedParameter); + return null; } @Override - default void visit(Concat concat) { - visit((BinaryExpression) concat); + public Void visit(NumericBind numericBind, S context) { + visit(numericBind); + return null; } @Override - default void visit(Matches matches) { - visit((BinaryExpression) matches); + public Void visit(DoubleValue doubleValue, S context) { + visit(doubleValue); + return null; } @Override - default void visit(BitwiseAnd bitwiseAnd) { - visit((BinaryExpression) bitwiseAnd); + public Void visit(BooleanValue booleanValue, S context) { + visit(booleanValue); + return null; } @Override - default void visit(BitwiseOr bitwiseOr) { - visit((BinaryExpression) bitwiseOr); + public Void visit(LongValue longValue, S context) { + visit(longValue); + return null; } @Override - default void visit(BitwiseXor bitwiseXor) { - visit((BinaryExpression) bitwiseXor); + public Void visit(HexValue hexValue, S context) { + visit(hexValue); + return null; } @Override - default void visit(CastExpression cast) { - + public Void visit(DateValue dateValue, S context) { + visit(dateValue); + return null; } @Override - default void visit(Modulo modulo) { - visit((BinaryExpression) modulo); + public Void visit(TimeValue timeValue, S context) { + visit(timeValue); + return null; } @Override - default void visit(AnalyticExpression aexpr) { - + public Void visit(TimestampValue timestampValue, S context) { + visit(timestampValue); + return null; } @Override - default void visit(ExtractExpression eexpr) { - + public Void visit(StringValue stringValue, S context) { + visit(stringValue); + return null; } @Override - default void visit(IntervalExpression iexpr) { - + public Void visit(AndExpression andExpression, S context) { + visit(andExpression); + return null; } @Override - default void visit(OracleHierarchicalExpression oexpr) { - + public Void visit(OrExpression orExpression, S context) { + visit(orExpression); + return null; } @Override - default void visit(RegExpMatchOperator rexpr) { - visit((BinaryExpression) rexpr); + public Void visit(Between between, S context) { + visit(between); + return null; } @Override - default void visit(JsonExpression jsonExpr) { - + public Void visit(InExpression inExpression, S context) { + visit(inExpression); + return null; } @Override - default void visit(JsonOperator jsonExpr) { - + public Void visit(IsNullExpression isNullExpression, S context) { + visit(isNullExpression); + return null; } @Override - default void visit(RegExpMySQLOperator regExpMySQLOperator) { - visit((BinaryExpression) regExpMySQLOperator); + public Void visit(IsBooleanExpression isBooleanExpression, S context) { + visit(isBooleanExpression); + return null; } @Override - default void visit(UserVariable var) { - + public Void visit(Column column, S context) { + visit(column); + return null; } @Override - default void visit(NumericBind bind) { - + public Void visit(ParenthesedSelect select, S context) { + visit((Select) select); + return null; } @Override - default void visit(KeepExpression aexpr) { - + public Void visit(Select select, S context) { + visit(select); + return null; } @Override - default void visit(MySQLGroupConcat groupConcat) { - + public Void visit(CaseExpression caseExpression, S context) { + visit(caseExpression); + return null; } @Override - default void visit(ValueListExpression valueList) { - + public Void visit(ExistsExpression existsExpression, S context) { + visit(existsExpression); + return null; } @Override - default void visit(RowConstructor rowConstructor) { - + public Void visit(NotExpression notExpression, S context) { + visit(notExpression); + return null; } @Override - default void visit(OracleHint hint) { - + public Void visit(CastExpression castExpression, S context) { + visit(castExpression); + return null; } @Override - default void visit(TimeKeyExpression timeKeyExpression) { - + public Void visit(ArrayExpression arrayExpression, S context) { + visit(arrayExpression); + return null; } @Override - default void visit(DateTimeLiteralExpression literal) { - + public Void visit(ExpressionList expressionList, S context) { + if (expressionList instanceof Parenthesis) { + visit((Parenthesis) expressionList); + return null; + } + if (expressionList instanceof ParenthesedExpressionList && expressionList.size() == 1) { + expressionList.get(0).accept(this, context); + } + return null; } @Override - default void visit(NotExpression aThis) { - + public Void visit(IntervalExpression intervalExpression, S context) { + visit(intervalExpression); + return null; } @Override - default void visit(NextValExpression aThis) { - + public Void visit(UserVariable var, S context) { + visit(var); + return null; } @Override - default void visit(CollateExpression aThis) { - + public Void visit(EqualsTo equalsTo, S context) { + visit((BinaryExpression) equalsTo); + visit((ComparisonOperator) equalsTo); + return null; } @Override - default void visit(SimilarToExpression aThis) { - visit((BinaryExpression) aThis); + public Void visit(GreaterThan greaterThan, S context) { + visit((BinaryExpression) greaterThan); + visit((ComparisonOperator) greaterThan); + return null; } @Override - default void visit(ArrayExpression aThis) { - + public Void visit(GreaterThanEquals greaterThanEquals, S context) { + visit((BinaryExpression) greaterThanEquals); + visit((ComparisonOperator) greaterThanEquals); + return null; } @Override - default void visit(XMLSerializeExpr xmlSerializeExpr) { - + public Void visit(LikeExpression likeExpression, S context) { + visit((BinaryExpression) likeExpression); + return null; } @Override - default void visit(VariableAssignment variableAssignment) { - + public Void visit(MinorThan minorThan, S context) { + visit((BinaryExpression) minorThan); + visit((ComparisonOperator) minorThan); + return null; } @Override - default void visit(ArrayConstructor arrayConstructor) { - + public Void visit(MinorThanEquals minorThanEquals, S context) { + visit((BinaryExpression) minorThanEquals); + visit((ComparisonOperator) minorThanEquals); + return null; } @Override - default void visit(XorExpression xorExpression) { - + public Void visit(NotEqualsTo notEqualsTo, S context) { + visit((BinaryExpression) notEqualsTo); + visit((ComparisonOperator) notEqualsTo); + return null; } @Override - default void visit(RowGetExpression rowGetExpression) { - + public Void visit(Addition addition, S context) { + visit((BinaryExpression) addition); + return null; } @Override - default void visit(TimezoneExpression aThis) { - + public Void visit(Division division, S context) { + visit((BinaryExpression) division); + return null; } @Override - default void visit(OracleNamedFunctionParameter aThis) { - + public Void visit(IntegerDivision division, S context) { + visit((BinaryExpression) division); + return null; } @Override - default void visit(AllValue allValue) { - + public Void visit(Multiplication multiplication, S context) { + visit((BinaryExpression) multiplication); + return null; } @Override - default void visit(JsonFunction aThis) { - + public Void visit(Subtraction subtraction, S context) { + visit((BinaryExpression) subtraction); + return null; } @Override - default void visit(AllColumns allColumns) { - + public Void visit(Concat concat, S context) { + visit((BinaryExpression) concat); + return null; } @Override - default void visit(TryCastExpression cast) { - + public Void visit(Matches matches, S context) { + visit((BinaryExpression) matches); + return null; } @Override - default void visit(GeometryDistance geometryDistance) { - + public Void visit(BitwiseAnd bitwiseAnd, S context) { + visit((BinaryExpression) bitwiseAnd); + return null; } @Override - default void visit(SafeCastExpression cast) { - + public Void visit(BitwiseOr bitwiseOr, S context) { + visit((BinaryExpression) bitwiseOr); + return null; } @Override - default void visit(ConnectByRootOperator aThis) { - + public Void visit(BitwiseXor bitwiseXor, S context) { + visit((BinaryExpression) bitwiseXor); + return null; } @Override - default void visit(JsonAggregateFunction aThis) { - + public Void visit(Modulo modulo, S context) { + visit((BinaryExpression) modulo); + return null; } @Override - default void visit(AllTableColumns allTableColumns) { - + public Void visit(RegExpMatchOperator regExpMatchOperator, S context) { + visit((BinaryExpression) regExpMatchOperator); + return null; } @Override - default void visit(OverlapsCondition overlapsCondition) { - + public Void visit(SimilarToExpression similarToExpression, S context) { + visit((BinaryExpression) similarToExpression); + return null; } @Override - default void visit(IsDistinctExpression isDistinctExpression) { - + public Void visit(FromQuery fromQuery, S context) { + return null; } } diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectListAggFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectListAggFeature.java index 1409fe7..e019937 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectListAggFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectListAggFeature.java @@ -19,7 +19,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.statement.select.SubSelect; +import net.sf.jsqlparser.statement.select.Select; import org.apache.commons.collections.CollectionUtils; import org.jetlinks.reactor.ql.ReactorQLContext; import org.jetlinks.reactor.ql.ReactorQLMetadata; @@ -52,9 +52,9 @@ public Function, Flux> createMapper(Expression exp mapper = flux -> flux.map(ReactorQLRecord::getRecord); } else { Expression expr = function.getParameters().getExpressions().get(0); - if (expr instanceof SubSelect) { + if (expr instanceof Select) { Function> _mapper = - FromFeature.createFromMapperByFrom(((SubSelect) expr), metadata); + FromFeature.createFromMapperByFrom(((Select) expr), metadata); mapper = flux -> _mapper .apply(ReactorQLContext.ofDatasource((r) -> flux)) .map(ReactorQLRecord::getRecord); diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectRowAggMapFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectRowAggMapFeature.java index d0588d6..89118f4 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectRowAggMapFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/agg/CollectRowAggMapFeature.java @@ -22,6 +22,7 @@ import org.jetlinks.reactor.ql.feature.FeatureId; import org.jetlinks.reactor.ql.feature.ValueAggMapFeature; import org.jetlinks.reactor.ql.feature.ValueMapFeature; +import org.jetlinks.reactor.ql.utils.ExpressionUtils; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -40,9 +41,7 @@ public Function, Flux> createMapper(Expression exp net.sf.jsqlparser.expression.Function function = ((net.sf.jsqlparser.expression.Function) expression); List expressions; - if (function.getParameters() == null || CollectionUtils.isEmpty(expressions = function - .getParameters() - .getExpressions())) { + if (function.getParameters() == null || CollectionUtils.isEmpty(expressions = ExpressionUtils.getFunctionParameter(function))) { throw new IllegalArgumentException("函数参数不能为空:" + expression); } if (expressions.size() != 2) { diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/agg/MapAggFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/agg/MapAggFeature.java index 2a57ec4..b4cacfb 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/agg/MapAggFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/agg/MapAggFeature.java @@ -54,7 +54,7 @@ public MapAggFeature(String type, public Function, Flux> createMapper(Expression expression, ReactorQLMetadata metadata) { net.sf.jsqlparser.expression.Function function = ((net.sf.jsqlparser.expression.Function) expression); - List expressions = function.getParameters().getExpressions(); + List expressions = ExpressionUtils.getFunctionParameter(function); Expression exp = expressions.get(0); Function> columnMapper = ValueMapFeature.createMapperNow(exp, metadata); diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/distinct/DefaultDistinctFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/distinct/DefaultDistinctFeature.java index be60c25..b9552c5 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/distinct/DefaultDistinctFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/distinct/DefaultDistinctFeature.java @@ -36,34 +36,28 @@ public class DefaultDistinctFeature implements DistinctFeature { @Override public Function, Flux> createDistinctMapper(Distinct distinct, ReactorQLMetadata metadata) { - List items = distinct.getOnSelectItems(); + List> items = distinct.getOnSelectItems(); if (items == null) { return flux -> flux.distinct(ReactorQLRecord::getRecord); } List>> keySelector = new ArrayList<>(); - for (SelectItem item : items) { - item.accept(new SelectItemVisitor() { - @Override - public void visit(AllColumns allColumns) { - keySelector.add(record -> Mono.justOrEmpty(record.getRecord())); - } - - @Override - public void visit(AllTableColumns allTableColumns) { - String tname = allTableColumns.getTable().getAlias() != null ? allTableColumns - .getTable() - .getAlias() - .getName() : allTableColumns.getTable().getName(); - keySelector.add(record -> Mono.justOrEmpty(record.getRecord(tname))); - } - - @Override - public void visit(SelectExpressionItem selectExpressionItem) { - Expression expr = selectExpressionItem.getExpression(); - Function> mapper = ValueMapFeature.createMapperNow(expr, metadata); - keySelector.add(record -> Mono.from(mapper.apply(record))); - } - }); + for (SelectItem item : items) { + Expression expr = item.getExpression(); + if (expr instanceof AllColumns) { + keySelector.add(record -> Mono.justOrEmpty(record.getRecord())); + continue; + } + if (expr instanceof AllTableColumns) { + AllTableColumns allTableColumns = (AllTableColumns) expr; + String tname = allTableColumns.getTable().getAlias() != null ? allTableColumns + .getTable() + .getAlias() + .getName() : allTableColumns.getTable().getName(); + keySelector.add(record -> Mono.justOrEmpty(record.getRecord(tname))); + continue; + } + Function> mapper = ValueMapFeature.createMapperNow(expr, metadata); + keySelector.add(record -> Mono.from(mapper.apply(record))); } if (keySelector.isEmpty()) { return flux -> flux.distinct(ReactorQLRecord::getRecord); diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/filter/IfValueMapFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/filter/IfValueMapFeature.java index 98b557e..35b968d 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/filter/IfValueMapFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/filter/IfValueMapFeature.java @@ -22,6 +22,7 @@ import org.jetlinks.reactor.ql.feature.FeatureId; import org.jetlinks.reactor.ql.feature.FilterFeature; import org.jetlinks.reactor.ql.feature.ValueMapFeature; +import org.jetlinks.reactor.ql.utils.ExpressionUtils; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; @@ -38,7 +39,7 @@ public Function> createMapper(Expression expression net.sf.jsqlparser.expression.Function function = ((net.sf.jsqlparser.expression.Function) expression); List expressions; - if (function.getParameters() == null || CollectionUtils.isEmpty(expressions = function.getParameters().getExpressions()) || expressions.size() < 2) { + if (function.getParameters() == null || CollectionUtils.isEmpty(expressions = ExpressionUtils.getFunctionParameter(function)) || expressions.size() < 2) { throw new IllegalArgumentException("函数参数数量必须>=2:" + expression); } diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/filter/InFilter.java b/src/main/java/org/jetlinks/reactor/ql/supports/filter/InFilter.java index e3d5735..ce88356 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/filter/InFilter.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/filter/InFilter.java @@ -18,8 +18,8 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.expression.operators.relational.InExpression; -import net.sf.jsqlparser.expression.operators.relational.ItemsList; -import net.sf.jsqlparser.statement.select.SubSelect; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; +import net.sf.jsqlparser.statement.select.Select; import org.jetlinks.reactor.ql.ReactorQLMetadata; import org.jetlinks.reactor.ql.ReactorQLRecord; import org.jetlinks.reactor.ql.feature.FeatureId; @@ -48,21 +48,23 @@ public BiFunction> createPredicate(Expres Expression left = inExpression.getLeftExpression(); Expression right = inExpression.getRightExpression(); - ItemsList in = (inExpression.getRightItemsList()); - List>> rightMappers = new ArrayList<>(); - if (in instanceof ExpressionList) { - rightMappers.addAll(((ExpressionList) in) + if (right instanceof ExpressionList) { + rightMappers.addAll(((ExpressionList) right) .getExpressions() .stream() .map(exp -> ValueMapFeature.createMapperNow(exp, metadata)) .collect(Collectors.toList())); - } - if (in instanceof SubSelect) { - rightMappers.add(ValueMapFeature.createMapperNow(((SubSelect) in), metadata)); - } - if (null != right) { + } else if (right instanceof ParenthesedExpressionList) { + rightMappers.addAll(((ParenthesedExpressionList) right) + .getExpressions() + .stream() + .map(exp -> ValueMapFeature.createMapperNow(exp, metadata)) + .collect(Collectors.toList())); + } else if (right instanceof Select) { + rightMappers.add(ValueMapFeature.createMapperNow(((Select) right), metadata)); + } else if (null != right) { rightMappers.add(ValueMapFeature.createMapperNow(right, metadata)); } diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/from/FromValuesFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/from/FromValuesFeature.java index 17676ef..5a24fb5 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/from/FromValuesFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/from/FromValuesFeature.java @@ -16,13 +16,15 @@ package org.jetlinks.reactor.ql.supports.from; import lombok.Getter; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; -import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; -import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; +import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList; import net.sf.jsqlparser.statement.select.FromItem; -import net.sf.jsqlparser.statement.select.SubSelect; -import net.sf.jsqlparser.statement.select.ValuesList; +import net.sf.jsqlparser.statement.select.ParenthesedFromItem; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.Values; import org.jetlinks.reactor.ql.ReactorQLContext; import org.jetlinks.reactor.ql.ReactorQLMetadata; import org.jetlinks.reactor.ql.ReactorQLRecord; @@ -44,16 +46,25 @@ public class FromValuesFeature implements FromFeature { @Override public Function> createFromMapper(FromItem fromItem, ReactorQLMetadata metadata) { - - ValuesList values = ((ValuesList) fromItem); - + Values values; + Alias aliasInfo; + if (fromItem instanceof ParenthesedFromItem) { + ParenthesedFromItem parenthesed = ((ParenthesedFromItem) fromItem); + values = ((Values) parenthesed.getFromItem()); + aliasInfo = parenthesed.getAlias(); + } else { + values = ((Values) fromItem); + aliasInfo = values.getAlias(); + } List>> mappers = new ArrayList<>(); - values.getMultiExpressionList().accept(new MapperBuilder(metadata, mappers::add)); - String alias = values.getAlias() == null ? null : values.getAlias().getName(); - List columns = values.getColumnNames(); - if (columns == null && values.getAlias() != null && values.getAlias().getAliasColumns() != null) { - columns = values.getAlias().getAliasColumns().stream() + for (Object row : values.getExpressions()) { + MapperBuilder.acceptRow(row, metadata, mappers::add); + } + String alias = aliasInfo == null ? null : aliasInfo.getName(); + List columns = null; + if (aliasInfo != null && aliasInfo.getAliasColumns() != null) { + columns = aliasInfo.getAliasColumns().stream() .map(c -> c.name) .collect(Collectors.toList()); } @@ -80,7 +91,7 @@ public String getId() { return FeatureId.From.values.getId(); } - private static class MapperBuilder implements ItemsListVisitor { + private static class MapperBuilder { ReactorQLMetadata metadata; Consumer>> consumer; @@ -92,13 +103,34 @@ public MapperBuilder(ReactorQLMetadata metadata, Consumer>> mappers = new ArrayList<>(); - @Override - public void visit(SubSelect subSelect) { + static void acceptRow(Object row, + ReactorQLMetadata metadata, + Consumer>> consumer) { + MapperBuilder builder = new MapperBuilder(metadata, consumer); + if (row instanceof ParenthesedExpressionList) { + builder.visit(((ParenthesedExpressionList) row)); + return; + } + if (row instanceof ExpressionList) { + builder.visit(((ExpressionList) row)); + return; + } + if (row instanceof Select) { + builder.visit(((Select) row)); + return; + } + if (row instanceof Expression) { + builder.visit(new ExpressionList<>((Expression) row)); + return; + } + throw new UnsupportedOperationException("不支持的 values 表达式:" + row); + } + + public void visit(Select subSelect) { consumer.accept(FromFeature.createFromMapperByFrom(subSelect, metadata)); } - @Override - public void visit(ExpressionList expressionList) { + public void visit(ExpressionList expressionList) { Flux>> mappers = Flux.fromIterable(expressionList.getExpressions()) .map(expr -> ValueMapFeature.createMapperNow(expr, metadata)); @@ -108,18 +140,6 @@ public void visit(ExpressionList expressionList) { .addRecords(ctx.getParameters()))) .map(val -> ReactorQLRecord.newRecord(null, val, ctx))); } - - @Override - public void visit(NamedExpressionList namedExpressionList) { - - } - - @Override - public void visit(MultiExpressionList multiExprList) { - for (ExpressionList list : multiExprList.getExpressionLists()) { - list.accept(this); - } - } } } diff --git a/src/main/java/org/jetlinks/reactor/ql/supports/from/SubSelectFromFeature.java b/src/main/java/org/jetlinks/reactor/ql/supports/from/SubSelectFromFeature.java index dc906cb..2417193 100644 --- a/src/main/java/org/jetlinks/reactor/ql/supports/from/SubSelectFromFeature.java +++ b/src/main/java/org/jetlinks/reactor/ql/supports/from/SubSelectFromFeature.java @@ -35,17 +35,20 @@ public class SubSelectFromFeature implements FromFeature { - private Function> doCreateMapper(String alias, SelectBody body, ReactorQLMetadata metadata) { + private Function> doCreateMapper(String alias, Select body, ReactorQLMetadata metadata) { if (body instanceof PlainSelect) { DefaultReactorQL reactorQL = new DefaultReactorQL(new DefaultReactorQLMetadata(metadata, ((PlainSelect) body))); return ctx -> reactorQL.start(ctx).map(record -> record.resultToRecord(alias == null ? record.getName() : alias)); } + if (body instanceof ParenthesedSelect) { + return doCreateMapper(alias, ((ParenthesedSelect) body).getSelect(), metadata); + } if (body instanceof SetOperationList) { SetOperationList setOperation = ((SetOperationList) body); - List selects = setOperation.getSelects(); + List