From 0c580bb3836537887fe680644e98afe607e2da05 Mon Sep 17 00:00:00 2001 From: gyl <1554863190@qq.com> Date: Wed, 30 Sep 2026 15:26:37 +0800 Subject: [PATCH 1/4] =?UTF-8?q?perf(rdb):=20=E4=BC=98=E5=8C=96=20PostgreSQ?= =?UTF-8?q?L=20=E9=9D=9E=E7=A9=BA=E5=88=97=E8=AE=A1=E6=95=B0=E5=B9=B6?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E8=87=AA=E5=AE=9A=E4=B9=89=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 + .../postgres/PostgresqlSchemaMetadata.java | 2 + ...PostgresqlSelectColumnFragmentBuilder.java | 54 ++++++ ...gresqlSelectColumnFragmentBuilderTest.java | 160 ++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java create mode 100644 hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java diff --git a/README.md b/README.md index 8e7447eb..3865eaab 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,7 @@ List> dataList= operator.dml().query() # 使用 建议配合[hsweb4](https://github.com/hs-web/hsweb-framework/tree/4.0.x)使用. + +## PostgreSQL 聚合计数 + +PostgreSQL 方言对无关联查询中的普通 `count(列)`,在表元数据确认该列非空且使用内置 `count` 函数时生成等价的 `count(*)`,保留原聚合别名。可空列、未知列、带函数选项或使用自定义 `count` 函数的计数,以及显式或可能由逻辑外键引入关联的查询继续使用原有 SQL 生成逻辑。其他数据库方言不应用此优化;实际性能取决于索引和执行计划。 diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java index f5eb575f..6a2c04f1 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java @@ -47,6 +47,7 @@ public PostgresqlSchemaMetadata(String name) { @Override public void addTable(RDBTableMetadata metadata) { + metadata.addFeature(PostgresqlSelectColumnFragmentBuilder.of(metadata)); metadata.addFeature(new PostgresqlBatchUpsertOperator(metadata)); super.addTable(metadata); } @@ -54,6 +55,7 @@ public void addTable(RDBTableMetadata metadata) { @Override public RDBTableMetadata newTable(String name) { RDBTableMetadata metadata = super.newTable(name); + metadata.addFeature(PostgresqlSelectColumnFragmentBuilder.of(metadata)); metadata.addFeature(new PostgresqlBatchUpsertOperator(metadata)); metadata.setOnColumnAdded(column->{ if(column.getValueCodec() instanceof EnumValueCodec &&((EnumValueCodec) column.getValueCodec()).isToMask()){ diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java new file mode 100644 index 00000000..18e4a239 --- /dev/null +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java @@ -0,0 +1,54 @@ +package org.hswebframework.ezorm.rdb.supports.postgres; + +import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBFeatures; +import org.hswebframework.ezorm.rdb.metadata.TableOrViewMetadata; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.SqlFragments; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.function.FunctionFragmentBuilder; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.query.SelectColumnFragmentBuilder; +import org.hswebframework.ezorm.rdb.operator.dml.query.QueryOperatorParameter; +import org.hswebframework.ezorm.rdb.operator.dml.query.SelectColumn; + +import java.util.Optional; + +public class PostgresqlSelectColumnFragmentBuilder extends SelectColumnFragmentBuilder { + + private final TableOrViewMetadata metadata; + + private PostgresqlSelectColumnFragmentBuilder(TableOrViewMetadata metadata) { + super(metadata); + this.metadata = metadata; + } + + public static PostgresqlSelectColumnFragmentBuilder of(TableOrViewMetadata metadata) { + return new PostgresqlSelectColumnFragmentBuilder(metadata); + } + + @Override + public SqlFragments createFragments(QueryOperatorParameter parameter, SelectColumn column) { + if (!"count".equals(column.getFunction()) + || column.getColumn() == null + || column.getColumn().contains(".") + || (column.getOpts() != null && !column.getOpts().isEmpty()) + || !parameter.getJoins().isEmpty() + || !metadata.getForeignKeys().isEmpty() + || metadata.findFeature(FunctionFragmentBuilder.createFeatureId("count")).orElse(null) != RDBFeatures.count) { + return super.createFragments(parameter, column); + } + + Optional columnMetadata = metadata.findColumn(column.getColumn()); + if (columnMetadata.isEmpty() || !columnMetadata.get().isNotNull()) { + return super.createFragments(parameter, column); + } + + // JOIN 可能把表中非空的列扩展为 NULL;只有无 JOIN 的普通计数才等价于行数计数。 + String alias = column.getAlias(); + if (alias == null) { + alias = columnMetadata.get().getAlias(); + if (alias.contains(".")) { + return SqlFragments.of("count(*)", "as", alias); + } + } + return SqlFragments.of("count(*)", "as", metadata.getDialect().quote(alias, false)); + } +} diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java new file mode 100644 index 00000000..4be8179a --- /dev/null +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java @@ -0,0 +1,160 @@ +package org.hswebframework.ezorm.rdb.supports.postgres; + +import org.hswebframework.ezorm.rdb.metadata.JdbcDataType; +import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBDatabaseMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBFeatures; +import org.hswebframework.ezorm.rdb.metadata.RDBSchemaMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; +import org.hswebframework.ezorm.rdb.metadata.dialect.Dialect; +import org.hswebframework.ezorm.rdb.metadata.key.ForeignKeyBuilder; +import org.hswebframework.ezorm.rdb.operator.DefaultDatabaseOperator; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.function.SimpleFunctionFragmentBuilder; +import org.hswebframework.ezorm.rdb.operator.dml.Join; +import org.hswebframework.ezorm.rdb.operator.dml.JoinType; +import org.hswebframework.ezorm.rdb.operator.dml.query.QueryOperatorParameter; +import org.hswebframework.ezorm.rdb.operator.dml.query.SelectColumn; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.JDBCType; +import java.util.Collections; + +public class PostgresqlSelectColumnFragmentBuilderTest { + + private RDBDatabaseMetadata database; + private PostgresqlSchemaMetadata schema; + private RDBTableMetadata table; + + @Before + public void init() { + database = new RDBDatabaseMetadata(Dialect.POSTGRES); + schema = new PostgresqlSchemaMetadata("public"); + database.addSchema(schema); + database.setCurrentSchema(schema); + + table = schema.newTable("metrics"); + addColumn(table, "id", true); + addColumn(table, "value", false); + schema.addTable(table); + } + + @Test + public void shouldUseCountStarForNonNullColumnAndKeepAlias() { + String sql = querySql(database, "metrics", count("id", "total")); + + Assert.assertTrue(sql, sql.contains("count(*) as \"total\"")); + + String defaultAliasSql = querySql(database, "metrics", count("id", null)); + Assert.assertTrue(defaultAliasSql, defaultAliasSql.contains("count(*) as \"id\"")); + } + + @Test + public void shouldKeepCountColumnForNullableColumnAndDistinctCount() { + String nullableSql = querySql(database, "metrics", count("value", "total")); + Assert.assertTrue(nullableSql, nullableSql.contains("count( " + table.getColumnNow("value").getFullName() + " ) as \"total\"")); + + SelectColumn distinct = count("id", "uniqueId"); + distinct.option("distinct", true); + String distinctSql = querySql(database, "metrics", distinct); + Assert.assertTrue(distinctSql, distinctSql.contains("count( distinct " + table.getColumnNow("id").getFullName() + " ) as \"uniqueId\"")); + Assert.assertFalse(distinctSql, distinctSql.contains("count(*)")); + } + + @Test + public void shouldUseCustomCountFunctionFromSchemaOrTable() { + schema.addFeature(new SimpleFunctionFragmentBuilder("count", "schema_count", "自定义计数")); + String schemaSql = querySql(database, "metrics", count("id", "total")); + Assert.assertTrue(schemaSql, schemaSql.contains("schema_count( " + table.getColumnNow("id").getFullName() + " ) as \"total\"")); + Assert.assertFalse(schemaSql, schemaSql.contains("count(*)")); + + table.addFeature(new SimpleFunctionFragmentBuilder("count", "table_count", "自定义计数")); + String tableSql = querySql(database, "metrics", count("id", "total")); + Assert.assertTrue(tableSql, tableSql.contains("table_count( " + table.getColumnNow("id").getFullName() + " ) as \"total\"")); + Assert.assertFalse(tableSql, tableSql.contains("count(*)")); + } + + @Test + public void shouldKeepCountColumnWhenQueryHasJoinOrLogicalForeignKey() { + QueryOperatorParameter parameter = new QueryOperatorParameter(); + parameter.setSelect(Collections.singletonList(count("id", "total"))); + Join join = new Join(); + join.setType(JoinType.right); + join.setTarget("details"); + parameter.getJoins().add(join); + + String joinedSql = table.findFeatureNow(RDBFeatures.select).createFragments(parameter).toRequest().getSql(); + Assert.assertTrue(joinedSql, joinedSql.contains("count( " + table.getColumnNow("id").getFullName() + " )")); + Assert.assertFalse(joinedSql, joinedSql.contains("count(*)")); + + parameter.getJoins().clear(); + RDBTableMetadata details = schema.newTable("details"); + addColumn(details, "metric_id", false); + schema.addTable(details); + table.addForeignKey(ForeignKeyBuilder.builder().target("details").autoJoin(true).build().addColumn("id", "metric_id")); + + String foreignKeySql = table.findFeatureNow(RDBFeatures.select).createFragments(parameter).toRequest().getSql(); + Assert.assertTrue(foreignKeySql, foreignKeySql.contains("count( " + table.getColumnNow("id").getFullName() + " )")); + Assert.assertFalse(foreignKeySql, foreignKeySql.contains("count(*)")); + } + + @Test + public void shouldRegisterBuilderWhenTableIsCreated() { + RDBTableMetadata created = schema.newTable("new_metrics"); + addColumn(created, "id", true); + + QueryOperatorParameter parameter = new QueryOperatorParameter(); + parameter.setSelect(Collections.singletonList(count("id", "total"))); + String sql = created.findFeatureNow(RDBFeatures.select).createFragments(parameter).toRequest().getSql(); + Assert.assertTrue(sql, sql.contains("count(*) as \"total\"")); + } + + @Test + public void shouldRegisterBuilderForLoadedTable() { + RDBTableMetadata loaded = new RDBTableMetadata("loaded_metrics"); + schema.addTable(loaded); + addColumn(loaded, "id", true); + + String sql = querySql(database, "loaded_metrics", count("id", "total")); + Assert.assertTrue(sql, sql.contains("count(*) as \"total\"")); + } + + @Test + public void shouldLeaveOtherDialectsUnchanged() { + RDBDatabaseMetadata h2Database = new RDBDatabaseMetadata(Dialect.H2); + RDBSchemaMetadata h2Schema = new RDBSchemaMetadata("PUBLIC"); + h2Database.addSchema(h2Schema); + h2Database.setCurrentSchema(h2Schema); + RDBTableMetadata h2Table = h2Schema.newTable("metrics"); + addColumn(h2Table, "id", true); + h2Schema.addTable(h2Table); + + String sql = querySql(h2Database, "metrics", count("id", "total")); + Assert.assertFalse(sql, sql.contains("count(*)")); + Assert.assertTrue(sql, sql.contains("count( " + h2Table.getColumnNow("id").getFullName() + " )")); + } + + private String querySql(RDBDatabaseMetadata database, String tableName, SelectColumn column) { + return DefaultDatabaseOperator.of(database) + .dml() + .query(tableName) + .select(column) + .getSql() + .getSql(); + } + + private SelectColumn count(String property, String alias) { + SelectColumn column = SelectColumn.of(property, alias); + column.setFunction("count"); + return column; + } + + private void addColumn(RDBTableMetadata table, String name, boolean notNull) { + RDBColumnMetadata column = table.newColumn(); + column.setName(name); + column.setType(JdbcDataType.of(JDBCType.VARCHAR, String.class)); + column.setNotNull(notNull); + table.addColumn(column); + } +} From 0f27a28b5375fb305859f94b363acbbd4f6c1a80 Mon Sep 17 00:00:00 2001 From: gyl <1554863190@qq.com> Date: Wed, 30 Sep 2026 16:52:08 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(rdb):=20=E6=94=AF=E6=8C=81=20countRows?= =?UTF-8?q?=20=E9=80=89=E9=A1=B9=E4=BC=98=E5=8C=96=E9=9D=9E=E7=A9=BA?= =?UTF-8?q?=E5=88=97=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- .../ezorm/rdb/metadata/RDBFeatures.java | 3 +- .../CountFunctionFragmentBuilder.java | 31 ++++ .../postgres/PostgresqlSchemaMetadata.java | 2 - ...PostgresqlSelectColumnFragmentBuilder.java | 54 ------ .../CountFunctionFragmentBuilderTest.java | 119 +++++++++++++ ...gresqlSelectColumnFragmentBuilderTest.java | 160 ------------------ 7 files changed, 154 insertions(+), 219 deletions(-) create mode 100644 hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java delete mode 100644 hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java create mode 100644 hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java delete mode 100644 hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java diff --git a/README.md b/README.md index 3865eaab..86383695 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,6 @@ List> dataList= operator.dml().query() 建议配合[hsweb4](https://github.com/hs-web/hsweb-framework/tree/4.0.x)使用. -## PostgreSQL 聚合计数 +## 聚合计数 -PostgreSQL 方言对无关联查询中的普通 `count(列)`,在表元数据确认该列非空且使用内置 `count` 函数时生成等价的 `count(*)`,保留原聚合别名。可空列、未知列、带函数选项或使用自定义 `count` 函数的计数,以及显式或可能由逻辑外键引入关联的查询继续使用原有 SQL 生成逻辑。其他数据库方言不应用此优化;实际性能取决于索引和执行计划。 +内置 `count` 函数默认生成 `count(列)`。查询方可通过 `SelectColumn.option("countRows", true)` 显式请求行数计数;表元数据确认目标列非空且没有其他函数选项时生成 `count(*)`,其他情况沿用原有生成逻辑。调用方需确保关联查询不会将目标列扩展为 NULL。自定义 `count` 函数仍按其自身实现处理该选项;旧版本不识别 `countRows` 时继续生成 `count(列)`。实际性能取决于索引和执行计划。 diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBFeatures.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBFeatures.java index be993619..985dd04f 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBFeatures.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/metadata/RDBFeatures.java @@ -2,6 +2,7 @@ import org.hswebframework.ezorm.core.FeatureId; import org.hswebframework.ezorm.core.param.TermType; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.function.CountFunctionFragmentBuilder; import org.hswebframework.ezorm.rdb.operator.builder.fragments.function.SimpleFunctionFragmentBuilder; import org.hswebframework.ezorm.rdb.operator.builder.fragments.query.QuerySqlBuilder; import org.hswebframework.ezorm.rdb.operator.builder.fragments.query.QuerySqlFragmentBuilder; @@ -51,7 +52,7 @@ public interface RDBFeatures { /*===============函数==============*/ /*==聚合函数==*/ - SimpleFunctionFragmentBuilder count = new SimpleFunctionFragmentBuilder("count", "计数"); + SimpleFunctionFragmentBuilder count = new CountFunctionFragmentBuilder(); SimpleFunctionFragmentBuilder sum = new SimpleFunctionFragmentBuilder("sum", "求和"); SimpleFunctionFragmentBuilder avg = new SimpleFunctionFragmentBuilder("avg", "平均值"); SimpleFunctionFragmentBuilder max = new SimpleFunctionFragmentBuilder("max", "最大值"); diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java new file mode 100644 index 00000000..33f86a43 --- /dev/null +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java @@ -0,0 +1,31 @@ +package org.hswebframework.ezorm.rdb.operator.builder.fragments.function; + +import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; +import org.hswebframework.ezorm.rdb.operator.builder.fragments.SqlFragments; + +import java.util.Map; + +/** + * 内置 count 函数。调用方明确启用 countRows,且查询不会使非空列因关联变为 NULL 时, + * 可以将列计数生成为行数计数;其他情况沿用普通 count 的行为。 + */ +public class CountFunctionFragmentBuilder extends SimpleFunctionFragmentBuilder { + + public static final String COUNT_ROWS = "countRows"; + + public CountFunctionFragmentBuilder() { + super("count", "计数"); + } + + @Override + public SqlFragments create(String columnFullName, RDBColumnMetadata metadata, Map opts) { + if (columnFullName != null + && metadata != null + && metadata.isNotNull() + && opts != null + && Boolean.TRUE.equals(opts.get(COUNT_ROWS))) { + return SqlFragments.single("count(*)"); + } + return super.create(columnFullName, metadata, opts); + } +} diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java index 6a2c04f1..f5eb575f 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSchemaMetadata.java @@ -47,7 +47,6 @@ public PostgresqlSchemaMetadata(String name) { @Override public void addTable(RDBTableMetadata metadata) { - metadata.addFeature(PostgresqlSelectColumnFragmentBuilder.of(metadata)); metadata.addFeature(new PostgresqlBatchUpsertOperator(metadata)); super.addTable(metadata); } @@ -55,7 +54,6 @@ public void addTable(RDBTableMetadata metadata) { @Override public RDBTableMetadata newTable(String name) { RDBTableMetadata metadata = super.newTable(name); - metadata.addFeature(PostgresqlSelectColumnFragmentBuilder.of(metadata)); metadata.addFeature(new PostgresqlBatchUpsertOperator(metadata)); metadata.setOnColumnAdded(column->{ if(column.getValueCodec() instanceof EnumValueCodec &&((EnumValueCodec) column.getValueCodec()).isToMask()){ diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java deleted file mode 100644 index 18e4a239..00000000 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilder.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.hswebframework.ezorm.rdb.supports.postgres; - -import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; -import org.hswebframework.ezorm.rdb.metadata.RDBFeatures; -import org.hswebframework.ezorm.rdb.metadata.TableOrViewMetadata; -import org.hswebframework.ezorm.rdb.operator.builder.fragments.SqlFragments; -import org.hswebframework.ezorm.rdb.operator.builder.fragments.function.FunctionFragmentBuilder; -import org.hswebframework.ezorm.rdb.operator.builder.fragments.query.SelectColumnFragmentBuilder; -import org.hswebframework.ezorm.rdb.operator.dml.query.QueryOperatorParameter; -import org.hswebframework.ezorm.rdb.operator.dml.query.SelectColumn; - -import java.util.Optional; - -public class PostgresqlSelectColumnFragmentBuilder extends SelectColumnFragmentBuilder { - - private final TableOrViewMetadata metadata; - - private PostgresqlSelectColumnFragmentBuilder(TableOrViewMetadata metadata) { - super(metadata); - this.metadata = metadata; - } - - public static PostgresqlSelectColumnFragmentBuilder of(TableOrViewMetadata metadata) { - return new PostgresqlSelectColumnFragmentBuilder(metadata); - } - - @Override - public SqlFragments createFragments(QueryOperatorParameter parameter, SelectColumn column) { - if (!"count".equals(column.getFunction()) - || column.getColumn() == null - || column.getColumn().contains(".") - || (column.getOpts() != null && !column.getOpts().isEmpty()) - || !parameter.getJoins().isEmpty() - || !metadata.getForeignKeys().isEmpty() - || metadata.findFeature(FunctionFragmentBuilder.createFeatureId("count")).orElse(null) != RDBFeatures.count) { - return super.createFragments(parameter, column); - } - - Optional columnMetadata = metadata.findColumn(column.getColumn()); - if (columnMetadata.isEmpty() || !columnMetadata.get().isNotNull()) { - return super.createFragments(parameter, column); - } - - // JOIN 可能把表中非空的列扩展为 NULL;只有无 JOIN 的普通计数才等价于行数计数。 - String alias = column.getAlias(); - if (alias == null) { - alias = columnMetadata.get().getAlias(); - if (alias.contains(".")) { - return SqlFragments.of("count(*)", "as", alias); - } - } - return SqlFragments.of("count(*)", "as", metadata.getDialect().quote(alias, false)); - } -} diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java new file mode 100644 index 00000000..0ed59627 --- /dev/null +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java @@ -0,0 +1,119 @@ +package org.hswebframework.ezorm.rdb.operator.builder.fragments.function; + +import org.hswebframework.ezorm.rdb.metadata.JdbcDataType; +import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBDatabaseMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; +import org.hswebframework.ezorm.rdb.metadata.dialect.Dialect; +import org.hswebframework.ezorm.rdb.operator.DefaultDatabaseOperator; +import org.hswebframework.ezorm.rdb.operator.dml.query.SelectColumn; +import org.hswebframework.ezorm.rdb.supports.postgres.PostgresqlSchemaMetadata; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.JDBCType; + +public class CountFunctionFragmentBuilderTest { + + private RDBDatabaseMetadata database; + private PostgresqlSchemaMetadata schema; + private RDBTableMetadata table; + + @Before + public void init() { + database = new RDBDatabaseMetadata(Dialect.POSTGRES); + schema = new PostgresqlSchemaMetadata("public"); + database.addSchema(schema); + database.setCurrentSchema(schema); + + table = schema.newTable("metrics"); + addColumn("id", true); + addColumn("value", false); + schema.addTable(table); + } + + @Test + public void shouldOnlyRewriteRequestedNonNullCount() { + String normalSql = querySql(count("id", "total")); + Assert.assertTrue(normalSql, normalSql.contains("count( " + table.getColumnNow("id").getFullName() + " ) as \"total\"")); + Assert.assertFalse(normalSql, normalSql.contains("count(*)")); + + SelectColumn countRows = count("id", "total"); + countRows.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + String rowsSql = querySql(countRows); + Assert.assertTrue(rowsSql, rowsSql.contains("count(*) as \"total\"")); + } + + @Test + public void shouldKeepNullableColumnCount() { + SelectColumn countRows = count("value", "total"); + countRows.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + + String sql = querySql(countRows); + Assert.assertTrue(sql, sql.contains("count( " + table.getColumnNow("value").getFullName() + " ) as \"total\"")); + Assert.assertFalse(sql, sql.contains("count(*)")); + } + + @Test + public void shouldKeepDistinctAndArgOptions() { + SelectColumn distinct = count("id", "total"); + distinct.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + distinct.option("distinct", true); + String distinctSql = querySql(distinct); + Assert.assertTrue(distinctSql, distinctSql.contains("count( distinct " + table.getColumnNow("id").getFullName() + " )")); + + SelectColumn arg = count("id", "total"); + arg.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + arg.option("arg", 1); + String argSql = querySql(arg); + Assert.assertTrue(argSql, argSql.contains("count( 1 ) as \"total\"")); + } + + @Test + public void shouldRespectCustomCountFunction() { + schema.addFeature(new SimpleFunctionFragmentBuilder("count", "schema_count", "自定义计数")); + SelectColumn countRows = count("id", "total"); + countRows.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + String schemaSql = querySql(countRows); + Assert.assertTrue(schemaSql, schemaSql.contains("schema_count( " + table.getColumnNow("id").getFullName() + " )")); + + table.addFeature(new SimpleFunctionFragmentBuilder("count", "table_count", "自定义计数")); + String tableSql = querySql(countRows); + Assert.assertTrue(tableSql, tableSql.contains("table_count( " + table.getColumnNow("id").getFullName() + " )")); + } + + @Test + public void shouldKeepLegacyBuilderBehaviorForUnknownOption() { + schema.addFeature(new SimpleFunctionFragmentBuilder("count", "计数")); + SelectColumn countRows = count("id", "total"); + countRows.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + + String sql = querySql(countRows); + Assert.assertTrue(sql, sql.contains("count( " + table.getColumnNow("id").getFullName() + " )")); + Assert.assertFalse(sql, sql.contains("count(*)")); + } + + private String querySql(SelectColumn column) { + return DefaultDatabaseOperator.of(database) + .dml() + .query("metrics") + .select(column) + .getSql() + .getSql(); + } + + private SelectColumn count(String property, String alias) { + SelectColumn column = SelectColumn.of(property, alias); + column.setFunction("count"); + return column; + } + + private void addColumn(String name, boolean notNull) { + RDBColumnMetadata column = table.newColumn(); + column.setName(name); + column.setType(JdbcDataType.of(JDBCType.VARCHAR, String.class)); + column.setNotNull(notNull); + table.addColumn(column); + } +} diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java deleted file mode 100644 index 4be8179a..00000000 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/supports/postgres/PostgresqlSelectColumnFragmentBuilderTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package org.hswebframework.ezorm.rdb.supports.postgres; - -import org.hswebframework.ezorm.rdb.metadata.JdbcDataType; -import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; -import org.hswebframework.ezorm.rdb.metadata.RDBDatabaseMetadata; -import org.hswebframework.ezorm.rdb.metadata.RDBFeatures; -import org.hswebframework.ezorm.rdb.metadata.RDBSchemaMetadata; -import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; -import org.hswebframework.ezorm.rdb.metadata.dialect.Dialect; -import org.hswebframework.ezorm.rdb.metadata.key.ForeignKeyBuilder; -import org.hswebframework.ezorm.rdb.operator.DefaultDatabaseOperator; -import org.hswebframework.ezorm.rdb.operator.builder.fragments.function.SimpleFunctionFragmentBuilder; -import org.hswebframework.ezorm.rdb.operator.dml.Join; -import org.hswebframework.ezorm.rdb.operator.dml.JoinType; -import org.hswebframework.ezorm.rdb.operator.dml.query.QueryOperatorParameter; -import org.hswebframework.ezorm.rdb.operator.dml.query.SelectColumn; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import java.sql.JDBCType; -import java.util.Collections; - -public class PostgresqlSelectColumnFragmentBuilderTest { - - private RDBDatabaseMetadata database; - private PostgresqlSchemaMetadata schema; - private RDBTableMetadata table; - - @Before - public void init() { - database = new RDBDatabaseMetadata(Dialect.POSTGRES); - schema = new PostgresqlSchemaMetadata("public"); - database.addSchema(schema); - database.setCurrentSchema(schema); - - table = schema.newTable("metrics"); - addColumn(table, "id", true); - addColumn(table, "value", false); - schema.addTable(table); - } - - @Test - public void shouldUseCountStarForNonNullColumnAndKeepAlias() { - String sql = querySql(database, "metrics", count("id", "total")); - - Assert.assertTrue(sql, sql.contains("count(*) as \"total\"")); - - String defaultAliasSql = querySql(database, "metrics", count("id", null)); - Assert.assertTrue(defaultAliasSql, defaultAliasSql.contains("count(*) as \"id\"")); - } - - @Test - public void shouldKeepCountColumnForNullableColumnAndDistinctCount() { - String nullableSql = querySql(database, "metrics", count("value", "total")); - Assert.assertTrue(nullableSql, nullableSql.contains("count( " + table.getColumnNow("value").getFullName() + " ) as \"total\"")); - - SelectColumn distinct = count("id", "uniqueId"); - distinct.option("distinct", true); - String distinctSql = querySql(database, "metrics", distinct); - Assert.assertTrue(distinctSql, distinctSql.contains("count( distinct " + table.getColumnNow("id").getFullName() + " ) as \"uniqueId\"")); - Assert.assertFalse(distinctSql, distinctSql.contains("count(*)")); - } - - @Test - public void shouldUseCustomCountFunctionFromSchemaOrTable() { - schema.addFeature(new SimpleFunctionFragmentBuilder("count", "schema_count", "自定义计数")); - String schemaSql = querySql(database, "metrics", count("id", "total")); - Assert.assertTrue(schemaSql, schemaSql.contains("schema_count( " + table.getColumnNow("id").getFullName() + " ) as \"total\"")); - Assert.assertFalse(schemaSql, schemaSql.contains("count(*)")); - - table.addFeature(new SimpleFunctionFragmentBuilder("count", "table_count", "自定义计数")); - String tableSql = querySql(database, "metrics", count("id", "total")); - Assert.assertTrue(tableSql, tableSql.contains("table_count( " + table.getColumnNow("id").getFullName() + " ) as \"total\"")); - Assert.assertFalse(tableSql, tableSql.contains("count(*)")); - } - - @Test - public void shouldKeepCountColumnWhenQueryHasJoinOrLogicalForeignKey() { - QueryOperatorParameter parameter = new QueryOperatorParameter(); - parameter.setSelect(Collections.singletonList(count("id", "total"))); - Join join = new Join(); - join.setType(JoinType.right); - join.setTarget("details"); - parameter.getJoins().add(join); - - String joinedSql = table.findFeatureNow(RDBFeatures.select).createFragments(parameter).toRequest().getSql(); - Assert.assertTrue(joinedSql, joinedSql.contains("count( " + table.getColumnNow("id").getFullName() + " )")); - Assert.assertFalse(joinedSql, joinedSql.contains("count(*)")); - - parameter.getJoins().clear(); - RDBTableMetadata details = schema.newTable("details"); - addColumn(details, "metric_id", false); - schema.addTable(details); - table.addForeignKey(ForeignKeyBuilder.builder().target("details").autoJoin(true).build().addColumn("id", "metric_id")); - - String foreignKeySql = table.findFeatureNow(RDBFeatures.select).createFragments(parameter).toRequest().getSql(); - Assert.assertTrue(foreignKeySql, foreignKeySql.contains("count( " + table.getColumnNow("id").getFullName() + " )")); - Assert.assertFalse(foreignKeySql, foreignKeySql.contains("count(*)")); - } - - @Test - public void shouldRegisterBuilderWhenTableIsCreated() { - RDBTableMetadata created = schema.newTable("new_metrics"); - addColumn(created, "id", true); - - QueryOperatorParameter parameter = new QueryOperatorParameter(); - parameter.setSelect(Collections.singletonList(count("id", "total"))); - String sql = created.findFeatureNow(RDBFeatures.select).createFragments(parameter).toRequest().getSql(); - Assert.assertTrue(sql, sql.contains("count(*) as \"total\"")); - } - - @Test - public void shouldRegisterBuilderForLoadedTable() { - RDBTableMetadata loaded = new RDBTableMetadata("loaded_metrics"); - schema.addTable(loaded); - addColumn(loaded, "id", true); - - String sql = querySql(database, "loaded_metrics", count("id", "total")); - Assert.assertTrue(sql, sql.contains("count(*) as \"total\"")); - } - - @Test - public void shouldLeaveOtherDialectsUnchanged() { - RDBDatabaseMetadata h2Database = new RDBDatabaseMetadata(Dialect.H2); - RDBSchemaMetadata h2Schema = new RDBSchemaMetadata("PUBLIC"); - h2Database.addSchema(h2Schema); - h2Database.setCurrentSchema(h2Schema); - RDBTableMetadata h2Table = h2Schema.newTable("metrics"); - addColumn(h2Table, "id", true); - h2Schema.addTable(h2Table); - - String sql = querySql(h2Database, "metrics", count("id", "total")); - Assert.assertFalse(sql, sql.contains("count(*)")); - Assert.assertTrue(sql, sql.contains("count( " + h2Table.getColumnNow("id").getFullName() + " )")); - } - - private String querySql(RDBDatabaseMetadata database, String tableName, SelectColumn column) { - return DefaultDatabaseOperator.of(database) - .dml() - .query(tableName) - .select(column) - .getSql() - .getSql(); - } - - private SelectColumn count(String property, String alias) { - SelectColumn column = SelectColumn.of(property, alias); - column.setFunction("count"); - return column; - } - - private void addColumn(RDBTableMetadata table, String name, boolean notNull) { - RDBColumnMetadata column = table.newColumn(); - column.setName(name); - column.setType(JdbcDataType.of(JDBCType.VARCHAR, String.class)); - column.setNotNull(notNull); - table.addColumn(column); - } -} From db1f9c353aaf93af694dd859139434b6c6cbc3e3 Mon Sep 17 00:00:00 2001 From: gyl <1554863190@qq.com> Date: Wed, 30 Sep 2026 16:56:03 +0800 Subject: [PATCH 3/4] =?UTF-8?q?test(rdb):=20=E8=A1=A5=E5=85=85=20countRows?= =?UTF-8?q?=20=E9=80=89=E9=A1=B9=E8=BE=B9=E7=95=8C=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CountFunctionFragmentBuilderTest.java | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java index 0ed59627..8ae4230a 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java @@ -3,6 +3,8 @@ import org.hswebframework.ezorm.rdb.metadata.JdbcDataType; import org.hswebframework.ezorm.rdb.metadata.RDBColumnMetadata; import org.hswebframework.ezorm.rdb.metadata.RDBDatabaseMetadata; +import org.hswebframework.ezorm.rdb.metadata.RDBFeatures; +import org.hswebframework.ezorm.rdb.metadata.RDBSchemaMetadata; import org.hswebframework.ezorm.rdb.metadata.RDBTableMetadata; import org.hswebframework.ezorm.rdb.metadata.dialect.Dialect; import org.hswebframework.ezorm.rdb.operator.DefaultDatabaseOperator; @@ -13,6 +15,7 @@ import org.junit.Test; import java.sql.JDBCType; +import java.util.Collections; public class CountFunctionFragmentBuilderTest { @@ -28,8 +31,8 @@ public void init() { database.setCurrentSchema(schema); table = schema.newTable("metrics"); - addColumn("id", true); - addColumn("value", false); + addColumn(table, "id", true); + addColumn(table, "value", false); schema.addTable(table); } @@ -55,6 +58,20 @@ public void shouldKeepNullableColumnCount() { Assert.assertFalse(sql, sql.contains("count(*)")); } + @Test + public void shouldRequireEnabledOptionAndResolvedColumn() { + SelectColumn disabled = count("id", "total"); + disabled.option(CountFunctionFragmentBuilder.COUNT_ROWS, false); + String disabledSql = querySql(disabled); + Assert.assertTrue(disabledSql, disabledSql.contains("count( " + table.getColumnNow("id").getFullName() + " )")); + + Assert.assertTrue(RDBFeatures.count + .create(null, + table.getColumnNow("id"), + Collections.singletonMap(CountFunctionFragmentBuilder.COUNT_ROWS, true)) + .isEmpty()); + } + @Test public void shouldKeepDistinctAndArgOptions() { SelectColumn distinct = count("id", "total"); @@ -94,6 +111,35 @@ public void shouldKeepLegacyBuilderBehaviorForUnknownOption() { Assert.assertFalse(sql, sql.contains("count(*)")); } + @Test + public void shouldKeepOptInBehaviorForOtherDialects() { + RDBDatabaseMetadata h2Database = new RDBDatabaseMetadata(Dialect.H2); + RDBSchemaMetadata h2Schema = new RDBSchemaMetadata("PUBLIC"); + h2Database.addSchema(h2Schema); + h2Database.setCurrentSchema(h2Schema); + RDBTableMetadata h2Table = h2Schema.newTable("metrics"); + addColumn(h2Table, "id", true); + h2Schema.addTable(h2Table); + + String normalSql = DefaultDatabaseOperator.of(h2Database) + .dml() + .query("metrics") + .select(count("id", "total")) + .getSql() + .getSql(); + Assert.assertTrue(normalSql, normalSql.contains("count( " + h2Table.getColumnNow("id").getFullName() + " )")); + + SelectColumn countRows = count("id", "total"); + countRows.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + String rowsSql = DefaultDatabaseOperator.of(h2Database) + .dml() + .query("metrics") + .select(countRows) + .getSql() + .getSql(); + Assert.assertTrue(rowsSql, rowsSql.contains("count(*)")); + } + private String querySql(SelectColumn column) { return DefaultDatabaseOperator.of(database) .dml() @@ -109,11 +155,11 @@ private SelectColumn count(String property, String alias) { return column; } - private void addColumn(String name, boolean notNull) { - RDBColumnMetadata column = table.newColumn(); + private void addColumn(RDBTableMetadata target, String name, boolean notNull) { + RDBColumnMetadata column = target.newColumn(); column.setName(name); column.setType(JdbcDataType.of(JDBCType.VARCHAR, String.class)); column.setNotNull(notNull); - table.addColumn(column); + target.addColumn(column); } } From b589693756afdb7942e3a1897e7dc3894121c79b Mon Sep 17 00:00:00 2001 From: gyl <1554863190@qq.com> Date: Wed, 30 Sep 2026 17:02:23 +0800 Subject: [PATCH 4/4] =?UTF-8?q?test(rdb):=20=E6=8C=89=20countRows=20?= =?UTF-8?q?=E4=BC=98=E5=85=88=E7=BA=A7=E4=BF=AE=E6=AD=A3=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../CountFunctionFragmentBuilder.java | 2 +- .../CountFunctionFragmentBuilderTest.java | 30 ++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 86383695..a729c775 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,4 @@ List> dataList= operator.dml().query() ## 聚合计数 -内置 `count` 函数默认生成 `count(列)`。查询方可通过 `SelectColumn.option("countRows", true)` 显式请求行数计数;表元数据确认目标列非空且没有其他函数选项时生成 `count(*)`,其他情况沿用原有生成逻辑。调用方需确保关联查询不会将目标列扩展为 NULL。自定义 `count` 函数仍按其自身实现处理该选项;旧版本不识别 `countRows` 时继续生成 `count(列)`。实际性能取决于索引和执行计划。 +内置 `count` 函数默认生成 `count(列)`。查询方可通过 `SelectColumn.option("countRows", true)` 显式请求行数计数;表元数据确认目标列非空时生成 `count(*)`,此时 `countRows` 优先于 `distinct`、`arg` 等选项,其他情况沿用原有生成逻辑。调用方需确保关联查询不会将目标列扩展为 NULL。自定义 `count` 函数仍按其自身实现处理该选项;旧版本不识别 `countRows` 时继续按原有选项生成 SQL。实际性能取决于索引和执行计划。 diff --git a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java index 33f86a43..3aa35ae4 100644 --- a/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java +++ b/hsweb-easy-orm-rdb/src/main/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilder.java @@ -7,7 +7,7 @@ /** * 内置 count 函数。调用方明确启用 countRows,且查询不会使非空列因关联变为 NULL 时, - * 可以将列计数生成为行数计数;其他情况沿用普通 count 的行为。 + * 可以将列计数生成为行数计数,并优先于 distinct、arg 等选项;其他情况沿用普通 count 的行为。 */ public class CountFunctionFragmentBuilder extends SimpleFunctionFragmentBuilder { diff --git a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java index 8ae4230a..146a7a8e 100644 --- a/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java +++ b/hsweb-easy-orm-rdb/src/test/java/org/hswebframework/ezorm/rdb/operator/builder/fragments/function/CountFunctionFragmentBuilderTest.java @@ -73,14 +73,42 @@ public void shouldRequireEnabledOptionAndResolvedColumn() { } @Test - public void shouldKeepDistinctAndArgOptions() { + public void shouldPreferCountRowsOverDistinctAndArg() { SelectColumn distinct = count("id", "total"); distinct.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); distinct.option("distinct", true); String distinctSql = querySql(distinct); + Assert.assertTrue(distinctSql, distinctSql.contains("count(*) as \"total\"")); + + SelectColumn arg = count("id", "total"); + arg.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + arg.option("arg", 1); + String argSql = querySql(arg); + Assert.assertTrue(argSql, argSql.contains("count(*) as \"total\"")); + } + + @Test + public void shouldKeepDistinctAndArgWithoutCountRows() { + SelectColumn distinct = count("id", "total"); + distinct.option("distinct", true); + String distinctSql = querySql(distinct); Assert.assertTrue(distinctSql, distinctSql.contains("count( distinct " + table.getColumnNow("id").getFullName() + " )")); SelectColumn arg = count("id", "total"); + arg.option("arg", 1); + String argSql = querySql(arg); + Assert.assertTrue(argSql, argSql.contains("count( 1 ) as \"total\"")); + } + + @Test + public void shouldUseOriginalOptionsForNullableColumn() { + SelectColumn distinct = count("value", "total"); + distinct.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); + distinct.option("distinct", true); + String distinctSql = querySql(distinct); + Assert.assertTrue(distinctSql, distinctSql.contains("count( distinct " + table.getColumnNow("value").getFullName() + " )")); + + SelectColumn arg = count("value", "total"); arg.option(CountFunctionFragmentBuilder.COUNT_ROWS, true); arg.option("arg", 1); String argSql = querySql(arg);