diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java index bbd2c11943023..76345aa6895c1 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java @@ -55,6 +55,7 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SelectScope; +import org.apache.calcite.sql.validate.SqlQualified; import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.sql.validate.SqlValidatorImpl; import org.apache.calcite.sql.validate.SqlValidatorNamespace; @@ -368,10 +369,31 @@ private SqlNode rewriteTableToQuery(SqlNode from) { } /** {@inheritDoc} */ - @Override protected void addToSelectList(List list, Set aliases, - List> fieldList, SqlNode exp, SelectScope scope, boolean includeSystemVars) { - if (includeSystemVars || exp.getKind() != SqlKind.IDENTIFIER || !isSystemFieldName(deriveAlias(exp, 0))) - super.addToSelectList(list, aliases, fieldList, exp, scope, includeSystemVars); + @Override protected void addToSelectList( + List list, + Set aliases, + List> fieldList, + SqlNode exp, + SelectScope scope, + boolean includeSysVars + ) { + if (!includeSysVars && exp.getKind() == SqlKind.IDENTIFIER && isSystemFieldName(deriveAlias(exp, 0))) { + SqlQualified qualified = scope.fullyQualify((SqlIdentifier)exp); + + if (qualified.namespace == null) + return; + + if (qualified.namespace.getTable() != null) { + // If child is table and has only system fields, expand star to these fields. + // Otherwise, expand star to non-system fields only. + for (RelDataTypeField fld : qualified.namespace.getRowType().getFieldList()) { + if (!isSystemField(fld)) + return; + } + } + } + + super.addToSelectList(list, aliases, fieldList, exp, scope, includeSysVars); } /** {@inheritDoc} */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MetadataIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MetadataIntegrationTest.java index f2b18e7ffbd71..eea66fe70e835 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MetadataIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/MetadataIntegrationTest.java @@ -18,6 +18,10 @@ package org.apache.ignite.internal.processors.query.calcite.integration; +import java.util.Collections; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.configuration.CacheConfiguration; import org.junit.Test; import static java.util.stream.Collectors.joining; @@ -101,4 +105,36 @@ public void columnNames() { assertQuery("select 1, -1, 'some string' from person").columnNames("1", "-1", "'some string'").check(); } + + /** Test implicit system fields expand by star. */ + @Test + public void testSystemFieldsStarExpand() { + IgniteCache cache = client.createCache(new CacheConfiguration("test") + .setSqlSchema("PUBLIC") + .setQueryEntities( + Collections.singletonList(new QueryEntity() + .setTableName("test") + .setKeyType(Integer.class.getName()) + .setValueType(Integer.class.getName()) + ) + ) + ); + + cache.put(0, 0); + + assertQuery("select * from test") + .columnNames("_KEY", "_VAL").returns(0, 0).check(); + + assertQuery("select * from (select * from test)") + .columnNames("_KEY", "_VAL").returns(0, 0).check(); + + assertQuery("select _KEY, _VAL from (select * from test) as t") + .columnNames("_KEY", "_VAL").returns(0, 0).check(); + + assertQuery("select * from (select _KEY, _VAL from test) as t") + .columnNames("_KEY", "_VAL").returns(0, 0).check(); + + assertQuery("select * from (select _KEY, _VAL as OTHER from test) as t") + .columnNames("_KEY", "OTHER").returns(0, 0).check(); + } }