Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion hsweb-commons/hsweb-commons-crud/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
Expand Down Expand Up @@ -166,4 +172,4 @@
</dependency>
</dependencies>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,16 @@ public Select select() {

@Override
public Optional<Column> findColumn(String name) {
return Optional.ofNullable(getColumnMappings().get(name));
if (name == null) {
return Optional.empty();
}
Map<String, Column> mappings = getColumnMappings();
Column column = mappings.get(name);
if (column == null) {
// 精确别名优先,仅在未命中时兼容实体属性名到数据库列名的转换。
column = mappings.get(QueryHelperUtils.toSnake(name));
}
return Optional.ofNullable(column);
}

@Override
Expand Down Expand Up @@ -177,7 +186,7 @@ private Column getColumnOrSelectColumn(String name) {
return column;
}

return getColumnMappings().get(name);
return findColumn(name).orElse(null);
}

@SneakyThrows
Expand Down Expand Up @@ -652,7 +661,7 @@ public SqlFragments createTermFragments(QueryAnalyzerImpl impl, Term term) {
Table table = impl.select.table;
String column = term.getColumn();

Column col = impl.getColumnMappings().get(column);
Column col = impl.findColumn(column).orElse(null);
//
// if (col == null) {
// if (column.contains(".")) {
Expand Down Expand Up @@ -697,9 +706,12 @@ public SqlFragments createTermFragments(QueryAnalyzerImpl impl, Term term) {

String colName = col.metadata != null ? col.metadata.getRealName() : col.name;

String fullName = col.metadata != null
? col.getMetadata().getFullName(table.alias)
: table.alias + "." + dialect.quote(colName, false);
// CTE和子查询会复用真实表列元数据,其内部可能已缓存原表全名,
// 条件列必须以当前查询分析出的表别名为准。
String fullName = dialect.buildColumnFullName(
table.alias,
colName,
col.metadata != null && !col.metadata.realNameDetected());

return metadata
.findFeature(createFeatureId(term.getTermType()))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.hswebframework.web.crud.query;

import org.hswebframework.ezorm.core.param.Sort;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.ezorm.rdb.executor.SqlRequest;
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrappers;
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
import org.hswebframework.ezorm.core.param.Sort;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.crud.TestApplication;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -15,6 +17,7 @@
import reactor.test.StepVerifier;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

Expand All @@ -24,6 +27,17 @@ public class QueryAnalyzerImplTest {
@Autowired
private DatabaseOperator database;

@Before
public void clearTestData() {
database
.sql()
.reactive()
.update("delete from s_test")
.then()
.as(StepVerifier::create)
.verifyComplete();
}

/**
* 执行SQL并验证是否有错误
*/
Expand Down Expand Up @@ -762,6 +776,15 @@ public void testNestedUnion() {

@Test
public void testMultipleCTE() {
database
.getMetadata()
.getCurrentSchema()
.getTableOrView("s_test", false)
.orElseThrow()
.getColumn("name")
.orElseThrow()
.getFullName();

QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
database,
"WITH " +
Expand All @@ -779,6 +802,7 @@ public void testMultipleCTE() {
assertNotNull(request.getSql(), "SQL should be generated");
assertNotNull(request.getParameters(), "Parameters should be set");
assertTrue(request.getSql().contains("), cte2 AS"), "multiple CTEs should be separated by comma");
assertTrue(request.getSql().contains("cte1.\"NAME\""), "filter column should use current CTE alias");
executeAndVerify(request);
}

Expand Down Expand Up @@ -1128,4 +1152,69 @@ public void testCustomSortFunctionFallbackToColumn() {

executeAndVerify(request);
}

@Test
public void testCamelCaseFilterColumnFallback() {
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
database,
"select * from (select t.name as alarm_time from s_test t) a");

SqlRequest request = analyzer.refactor(
QueryParamEntity
.newQuery()
.and("alarmTime", "eq", "test")
.getParam());

assertTrue(analyzer.findColumn("alarmTime").isPresent());
assertTrue(request.getSql().contains("alarm_time"));
assertArrayEquals(new Object[]{"test"}, request.getParameters());
executeAndVerify(request);
}

@Test
public void testQualifiedCamelCaseNestedFilterColumnFallback() {
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
database,
"select * from (select t.name as alarm_time from s_test t) a");
Term nested = new Term()
.nest()
.and("a.alarmTime", "eq", "test")
.clone();
QueryParamEntity param = QueryParamEntity.of();
param.setTerms(List.of(nested));

SqlRequest request = analyzer.refactor(param);

assertTrue(request.getSql().contains("alarm_time"));
assertArrayEquals(new Object[]{"test"}, request.getParameters());
executeAndVerify(request);
}

@Test
public void testExactFilterAliasTakesPriorityOverSnakeCaseFallback() {
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
database,
"select * from (select t.name as alarm_time, t.age as alarmTime from s_test t) a");

QueryAnalyzer.Column column = analyzer.findColumn("alarmTime").orElseThrow();

assertEquals("alarmTime", column.getAlias());
}

@Test
public void testUnknownCamelCaseFilterColumnIsRejected() {
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
database,
"select * from (select t.name as alarm_time from s_test t) a");
QueryParamEntity param = QueryParamEntity
.newQuery()
.and("unknownAlarmTime", "eq", "test")
.getParam();

IllegalArgumentException error = assertThrows(
IllegalArgumentException.class,
() -> analyzer.refactor(param));

assertEquals("undefined column [unknownAlarmTime]", error.getMessage());
}
}
Loading