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

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

commit d86e55a50b447bb5c87ffe9610c91f2c81ab8753
Author: amashenkov <[email protected]>
AuthorDate: Mon May 12 13:49:47 2025 +0300

    Add __PART virtual column.
---
 .../sql/group1/syscolumns/system_columns.test      | 26 ++++++++++++
 .../engine/exec/TableRowConverterFactoryImpl.java  | 48 +++++++++++++++-------
 .../sql/engine/prepare/IgniteSqlValidator.java     |  3 +-
 .../sql/engine/schema/SqlSchemaManagerImpl.java    | 10 ++---
 .../ignite/internal/sql/engine/util/Commons.java   |  4 +-
 .../sql/engine/framework/TestBuilders.java         | 13 ++++++
 6 files changed, 83 insertions(+), 21 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..bab9739dc7d 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,16 +38,16 @@ 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.
      *
      * @param tableDescriptor Table descriptor.
-     * @param schemaRegistry Registry of all schemas known so far. Used in 
case table returned
-     *      a row in older version than required to make an upgrade.
-     * @param schemaDescriptor Actual schema descriptor. Used as a target 
schema to convert
-     *      rows from sql format to one accepted by underlying table.
+     * @param schemaRegistry Registry of all schemas known so far. Used in 
case table returned a row in older version than required
+     *         to make an upgrade.
+     * @param schemaDescriptor Actual schema descriptor. Used as a target 
schema to convert rows from sql format to one accepted by
+     *         underlying table.
      */
     public TableRowConverterFactoryImpl(
             TableDescriptor tableDescriptor,
@@ -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,25 @@ 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 columnIndex = 
requiredColumns.nextSetBit(schemaDescriptor.length());
+                columnIndex >= 0;
+                columnIndex = requiredColumns.nextSetBit(columnIndex + 1)
+        ) {
+            columnsMap.put(columnIndex, 
virtualColumnsFactory.get(columnIndex).apply(partId));
+
+            if (columnIndex == 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..a305f7a04ed 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
@@ -1291,7 +1291,8 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
 
     public static boolean isSystemFieldName(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.
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..e855a2baf2c 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);
 
@@ -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..43bbee14809 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
@@ -1086,6 +1086,19 @@ public class TestBuilders {
                             () -> {
                                 throw new AssertionError("Partition virtual 
column is generated by a function");
                             }
+                    ),
+                            new ColumnDescriptorImpl(
+                            Commons.PART_COL_NAME_LEGACY,
+                            false,
+                            true,
+                            true,
+                            false,
+                            columns.size(),
+                            NativeTypes.INT32,
+                            DefaultValueStrategy.DEFAULT_COMPUTED,
+                            () -> {
+                                throw new AssertionError("Partition virtual 
column is generated by a function");
+                            }
                     ))), distribution);
 
             Map<String, IgniteIndex> indexes = indexBuilders.stream()

Reply via email to