You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2025/05/14 08:09:26 UTC

(ignite-3) branch main updated: IGNITE-24936 Sql. Allow access of virtual column "__part" without quoting (#5796)

This is an automated email from the ASF dual-hosted git repository.

amashenkov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 4a12fb66cde IGNITE-24936 Sql. Allow access of virtual column "__part" without quoting (#5796)
4a12fb66cde is described below

commit 4a12fb66cde0a75a3f9b09eb017d912691f9062f
Author: Andrew V. Mashenkov <AM...@users.noreply.github.com>
AuthorDate: Wed May 14 11:09:20 2025 +0300

    IGNITE-24936 Sql. Allow access of virtual column "__part" without quoting (#5796)
---
 .../sql/group1/syscolumns/system_columns.test      | 26 ++++++++++++++++
 .../engine/exec/TableRowConverterFactoryImpl.java  | 36 ++++++++++++++++------
 .../sql/engine/prepare/IgniteSqlValidator.java     | 10 +++---
 .../prepare/ddl/DdlSqlToCommandConverter.java      |  4 +--
 .../sql/engine/schema/SqlSchemaManagerImpl.java    | 12 ++++----
 .../ignite/internal/sql/engine/util/Commons.java   |  4 ++-
 .../sql/engine/framework/TestBuilders.java         | 36 ++++++++++++++--------
 .../internal/sql/engine/util/QueryCheckerTest.java |  2 +-
 8 files changed, 94 insertions(+), 36 deletions(-)

diff --git a/modules/sql-engine/src/integrationTest/sql/group1/syscolumns/system_columns.test b/modules/sql-engine/src/integrationTest/sql/group1/syscolumns/system_columns.test
index a7bc0c296e2..a15d1ef01ec 100644
--- a/modules/sql-engine/src/integrationTest/sql/group1/syscolumns/system_columns.test
+++ b/modules/sql-engine/src/integrationTest/sql/group1/syscolumns/system_columns.test
@@ -12,6 +12,11 @@ insert into t0 values (101, 'val1'), (102, 'val2');
 statement ok
 insert into t0 values (103, 'val3');
 
+# statement error: Failed to validate query:
+statement error
+----
+insert into t0 (id, val, __part) values (104, 'val4', 1)
+
 # statement error: Failed to validate query:
 statement error
 ----
@@ -26,6 +31,21 @@ select * from t0
 102	val2
 103	val3
 
+query II rowsort
+select __part, id from t0
+----
+19	101
+17	102
+11	103
+
+query I rowsort
+select __part from t0
+----
+19
+17
+11
+
+# Select legacy partition system column
 query II rowsort
 select "__part", id from t0
 ----
@@ -40,6 +60,12 @@ select "__part" from t0
 17
 11
 
+query II rowsort
+select __part, "__part" id from t0
+----
+19	19
+17	17
+11	11
 
 # Partition system column in WHERE
 query IT
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterFactoryImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterFactoryImpl.java
index f187a47a049..a7425a02762 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterFactoryImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableRowConverterFactoryImpl.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.sql.engine.exec;
 
+import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
 import java.util.BitSet;
@@ -37,7 +38,7 @@ public class TableRowConverterFactoryImpl implements TableRowConverterFactory {
     private final SchemaDescriptor schemaDescriptor;
     private final TableRowConverter fullRowConverter;
     private final BitSet tableColumnSet;
-    private IntFunction<VirtualColumn> virtualColumnFactory;
+    private final Int2ObjectArrayMap<IntFunction<VirtualColumn>> virtualColumnsFactory = new Int2ObjectArrayMap<>();
 
     /**
      * Creates a factory from given schema and indexes of primary key.
@@ -64,13 +65,20 @@ public class TableRowConverterFactoryImpl implements TableRowConverterFactory {
         tableColumnSet = new BitSet();
         tableColumnSet.set(0, tableDescriptor.columnsCount());
 
-        ColumnDescriptor columnDescriptor = tableDescriptor.columnDescriptor(Commons.PART_COL_NAME);
-
-        if (columnDescriptor != null) {
-            assert columnDescriptor.virtual();
+        addVirtualColumn(tableDescriptor.columnDescriptor(Commons.PART_COL_NAME));
+        addVirtualColumn(tableDescriptor.columnDescriptor(Commons.PART_COL_NAME_LEGACY));
+    }
 
-            virtualColumnFactory = (partId) -> new VirtualColumn(columnDescriptor.logicalIndex(), NativeTypes.INT32, false, partId);
+    private void addVirtualColumn(@Nullable ColumnDescriptor columnDescriptor) {
+        if (columnDescriptor == null) {
+            return;
         }
+
+        assert columnDescriptor.virtual();
+
+        int columnIndex = columnDescriptor.logicalIndex();
+
+        virtualColumnsFactory.put(columnIndex, (partId) -> new VirtualColumn(columnIndex, NativeTypes.INT32, false, partId));
     }
 
     @Override
@@ -99,13 +107,21 @@ public class TableRowConverterFactoryImpl implements TableRowConverterFactory {
                 schemaRegistry,
                 schemaDescriptor,
                 requiredColumns,
-                requireVirtualColumn ? createVirtualColumns(partId) : Int2ObjectMaps.emptyMap()
+                requireVirtualColumn ? createVirtualColumns(requiredColumns, partId) : Int2ObjectMaps.emptyMap()
         );
     }
 
-    private Int2ObjectMap<VirtualColumn> createVirtualColumns(int partId) {
-        VirtualColumn column = virtualColumnFactory.apply(partId);
+    private Int2ObjectMap<VirtualColumn> createVirtualColumns(BitSet requiredColumns, int partId) {
+        Int2ObjectMap<VirtualColumn> columnsMap = new Int2ObjectArrayMap<>(virtualColumnsFactory.size());
+
+        for (int i = requiredColumns.nextSetBit(schemaDescriptor.length()); i >= 0; i = requiredColumns.nextSetBit(i + 1)) {
+            columnsMap.put(i, virtualColumnsFactory.get(i).apply(partId));
+
+            if (i == Integer.MAX_VALUE) {
+                break; // Avoid overflow.
+            }
+        }
 
-        return Int2ObjectMaps.singleton(column.columnIndex(), column);
+        return columnsMap;
     }
 }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
index cd7dc32fd07..c1fce8c4618 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
@@ -672,7 +672,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
     /** {@inheritDoc} */
     @Override protected void addToSelectList(List<SqlNode> list, Set<String> aliases,
             List<Map.Entry<String, RelDataType>> fieldList, SqlNode exp, SelectScope scope, boolean includeSystemVars) {
-        if (includeSystemVars || exp.getKind() != SqlKind.IDENTIFIER || !isSystemFieldName(deriveAlias(exp, 0))) {
+        if (includeSystemVars || exp.getKind() != SqlKind.IDENTIFIER || !isSystemColumnName(deriveAlias(exp, 0))) {
             super.addToSelectList(list, aliases, fieldList, exp, scope, includeSystemVars);
         }
     }
@@ -1289,9 +1289,11 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
         return (IgniteTypeFactory) typeFactory;
     }
 
-    public static boolean isSystemFieldName(String alias) {
+    /** Returns {@code true} if the given alias is a system column name, {@code false} otherwise. */
+    public static boolean isSystemColumnName(String alias) {
         return (Commons.implicitPkEnabled() && Commons.IMPLICIT_PK_COL_NAME.equals(alias))
-                || alias.equals(Commons.PART_COL_NAME);
+                || alias.equals(Commons.PART_COL_NAME)
+                || alias.equals(Commons.PART_COL_NAME_LEGACY);
     }
 
     // We use these scopes to filter out valid usages of a ROW operator.
@@ -1365,7 +1367,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
         if (call.getKind() == SqlKind.AS) {
             String alias = deriveAlias(call, 0);
 
-            if (isSystemFieldName(alias)) {
+            if (isSystemColumnName(alias)) {
                 throw newValidationError(call, IgniteResource.INSTANCE.illegalAlias(alias));
             }
         }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
index 84795431af7..cce60463dce 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
@@ -318,7 +318,7 @@ public class DdlSqlToCommandConverter {
             if (sqlNode instanceof SqlColumnDeclaration) {
                 String colName = ((SqlColumnDeclaration) sqlNode).name.getSimple();
 
-                if (IgniteSqlValidator.isSystemFieldName(colName)) {
+                if (IgniteSqlValidator.isSystemColumnName(colName)) {
                     throw new SqlException(STMT_VALIDATION_ERR, "Failed to validate query. "
                             + "Column '" + colName + "' is reserved name.");
                 }
@@ -494,7 +494,7 @@ public class DdlSqlToCommandConverter {
             Boolean nullable = col.dataType.getNullable();
 
             String colName = col.name.getSimple();
-            if (IgniteSqlValidator.isSystemFieldName(colName)) {
+            if (IgniteSqlValidator.isSystemColumnName(colName)) {
                 throw new SqlException(STMT_VALIDATION_ERR, "Failed to validate query. "
                         + "Column '" + colName + "' is reserved name.");
             }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
index 6fe85ed9155..4fc375227d6 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/schema/SqlSchemaManagerImpl.java
@@ -281,7 +281,7 @@ public class SqlSchemaManagerImpl implements SqlSchemaManager {
 
     private static TableDescriptor createTableDescriptorForTable(CatalogTableDescriptor descriptor) {
         List<CatalogTableColumnDescriptor> columns = descriptor.columns();
-        List<ColumnDescriptor> colDescriptors = new ArrayList<>(columns.size() + 1);
+        List<ColumnDescriptor> colDescriptors = new ArrayList<>(columns.size() + 2);
         Object2IntMap<String> columnToIndex = buildColumnToIndexMap(columns);
 
         for (int i = 0; i < columns.size(); i++) {
@@ -301,8 +301,8 @@ public class SqlSchemaManagerImpl implements SqlSchemaManager {
         }
 
         // Add virtual column.
-        ColumnDescriptorImpl partVirtualColumn = createPartitionVirtualColumn(columns.size());
-        colDescriptors.add(partVirtualColumn);
+        colDescriptors.add(createPartitionVirtualColumn(columns.size(), Commons.PART_COL_NAME));
+        colDescriptors.add(createPartitionVirtualColumn(columns.size() + 1, Commons.PART_COL_NAME_LEGACY));
 
         IgniteDistribution distribution = createDistribution(descriptor, columnToIndex);
 
@@ -321,7 +321,7 @@ public class SqlSchemaManagerImpl implements SqlSchemaManager {
     }
 
     private static Object2IntMap<String> buildColumnToIndexMap(List<CatalogTableColumnDescriptor> columns) {
-        Object2IntMap<String> columnToIndex = new Object2IntOpenHashMap<>(columns.size() + 1);
+        Object2IntMap<String> columnToIndex = new Object2IntOpenHashMap<>(columns.size() + 2);
 
         for (int i = 0; i < columns.size(); i++) {
             CatalogTableColumnDescriptor col = columns.get(i);
@@ -331,9 +331,9 @@ public class SqlSchemaManagerImpl implements SqlSchemaManager {
         return columnToIndex;
     }
 
-    private static ColumnDescriptorImpl createPartitionVirtualColumn(int logicalIndex) {
+    private static ColumnDescriptorImpl createPartitionVirtualColumn(int logicalIndex, String partColName) {
         return new ColumnDescriptorImpl(
-                Commons.PART_COL_NAME,
+                partColName,
                 false,
                 true,
                 true,
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
index 1f8d2b49cef..6bee87dc25a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
@@ -128,7 +128,9 @@ import org.jetbrains.annotations.TestOnly;
  */
 public final class Commons {
     public static final String IMPLICIT_PK_COL_NAME = "__p_key";
-    public static final String PART_COL_NAME = "__part";
+    public static final String PART_COL_NAME = "__PART";
+    // Old name for partition column. Kept for backward compatibility.
+    public static final String PART_COL_NAME_LEGACY = "__part";
 
     public static final int IN_BUFFER_SIZE = 512;
 
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
index fb01f62fabf..4f186e351d3 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/framework/TestBuilders.java
@@ -1075,18 +1075,30 @@ public class TestBuilders {
 
             TableDescriptorImpl tableDescriptor = new TableDescriptorImpl(CollectionUtils.concat(columns,
                     List.of(new ColumnDescriptorImpl(
-                            Commons.PART_COL_NAME,
-                            false,
-                            true,
-                            true,
-                            false,
-                            columns.size(),
-                            NativeTypes.INT32,
-                            DefaultValueStrategy.DEFAULT_COMPUTED,
-                            () -> {
-                                throw new AssertionError("Partition virtual column is generated by a function");
-                            }
-                    ))), distribution);
+                                    Commons.PART_COL_NAME,
+                                    false,
+                                    true,
+                                    true,
+                                    false,
+                                    columns.size(),
+                                    NativeTypes.INT32,
+                                    DefaultValueStrategy.DEFAULT_COMPUTED,
+                                    () -> {
+                                        throw new AssertionError("Partition virtual column is generated by a function");
+                                    }),
+                            new ColumnDescriptorImpl(
+                                    Commons.PART_COL_NAME_LEGACY,
+                                    false,
+                                    true,
+                                    true,
+                                    false,
+                                    columns.size() + 1,
+                                    NativeTypes.INT32,
+                                    DefaultValueStrategy.DEFAULT_COMPUTED,
+                                    () -> {
+                                        throw new AssertionError("Partition virtual column is generated by a function");
+                                    }
+                            ))), distribution);
 
             Map<String, IgniteIndex> indexes = indexBuilders.stream()
                     .map(idx -> idx.build(tableDescriptor))
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
index e805f1492b8..00f50fc318b 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/util/QueryCheckerTest.java
@@ -85,7 +85,7 @@ public class QueryCheckerTest extends BaseIgniteAbstractTest {
                 .mapToObj(i -> List.of("N1"))
                 .collect(Collectors.toList()));
         CLUSTER.setDataProvider("T1", TestBuilders.tableScan(DataProvider.fromCollection(
-                List.of(new Object[]{1, 1, 1}, new Object[]{2, 2, 1})
+                List.of(new Object[]{1, 1, 1, 1}, new Object[]{2, 2, 1, 1})
         )));
     }