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

tkalkirill pushed a commit to branch ignite-28223-add-rowid
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-28223-add-rowid by this 
push:
     new d7f08aa28e3 IGNITE-28223-add-rowid wip
d7f08aa28e3 is described below

commit d7f08aa28e32d5c70bb75ce54ef2489fa1a16d97
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Mon Mar 23 10:32:37 2026 +0300

    IGNITE-28223-add-rowid wip
---
 .../calcite/integration/RowIdPseudoColumnTest.java | 233 +++++++++++++++++++--
 1 file changed, 211 insertions(+), 22 deletions(-)

diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RowIdPseudoColumnTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RowIdPseudoColumnTest.java
index abf07392f40..3707200d081 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RowIdPseudoColumnTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/RowIdPseudoColumnTest.java
@@ -17,10 +17,15 @@
 
 package org.apache.ignite.internal.processors.query.calcite.integration;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Base64;
 import java.util.List;
 import org.apache.calcite.adapter.enumerable.NullPolicy;
 import org.apache.calcite.linq4j.tree.Expressions;
-import org.apache.calcite.linq4j.tree.UnaryExpression;
 import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlFunction;
@@ -32,13 +37,14 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;
 import org.apache.calcite.sql.util.SqlOperatorTables;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.tools.FrameworkConfig;
 import org.apache.calcite.tools.Frameworks;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.Ignition;
+import org.apache.ignite.IgniteException;
 import org.apache.ignite.calcite.CalciteQueryEngineConfiguration;
 import org.apache.ignite.calcite.PseudoColumnDescriptor;
 import org.apache.ignite.calcite.PseudoColumnProvider;
@@ -48,12 +54,17 @@ import org.apache.ignite.configuration.SqlConfiguration;
 import org.apache.ignite.indexing.IndexingQueryEngineConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgnitionEx;
+import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.query.QueryUtils;
 import 
org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor;
+import 
org.apache.ignite.internal.processors.query.calcite.PseudoColumnValueExtractorContextEx;
 import org.apache.ignite.internal.processors.query.calcite.QueryChecker;
 import 
org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
 import 
org.apache.ignite.internal.processors.query.calcite.exec.exp.RexImpTable;
 import 
org.apache.ignite.internal.processors.query.calcite.prepare.IgniteSqlCallRewriteTable;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.plugin.AbstractTestPluginProvider;
 import org.apache.ignite.plugin.PluginContext;
 import org.jetbrains.annotations.Nullable;
@@ -85,18 +96,76 @@ public class RowIdPseudoColumnTest extends 
AbstractBasicIntegrationTest {
         for (int i = 0; i < 2; i++)
             sql("insert into PUBLIC.PERSON(id, name) values(?, ?)", i, "foo" + 
i);
 
-        assertQuery("select id, name, rowid from PUBLIC.PERSON where rowid = 
'0'")
+        List<List<?>> selectRs = sql("select rowid from PUBLIC.PERSON order by 
id");
+        Object rowId0 = selectRs.get(0).get(0);
+        Object rowId1 = selectRs.get(1).get(0);
+
+        assertQuery("select id, name, rowid from PUBLIC.PERSON where rowid = 
?")
+            .withParams(rowId0)
+            .columnNames("ID", "NAME", "ROWID")
+            .matches(QueryChecker.containsIndexScan("PUBLIC", "PERSON", 
"_key_PK"))
+            .returns(0, "foo0", rowId0)
+            .check();
+
+        assertQuery("select p.id, p.name, p.rowid from PUBLIC.PERSON as p 
where p.rowid = ?")
+            .withParams(rowId1)
             .columnNames("ID", "NAME", "ROWID")
             .matches(QueryChecker.containsIndexScan("PUBLIC", "PERSON", 
"_key_PK"))
-            .returns(0, "foo0", "0")
+            .returns(1, "foo1", rowId1)
+            .check();
+    }
+
+    @Test
+    public void testCompositePrimaryKey() {
+        sql("create table PUBLIC.PERSON(id int, name varchar, age int, primary 
key (id, name))");
+
+        for (int i = 0; i < 2; i++)
+            sql("insert into PUBLIC.PERSON(id, name, age) values(?, ?, ?)", i, 
"foo" + i, 18 + i);
+
+        List<List<?>> selectRs = sql("select rowid, _key from PUBLIC.PERSON 
order by id");
+        Object rowId0 = selectRs.get(0).get(0);
+        Object rowId1 = selectRs.get(1).get(0);
+
+        // TODO: IGNITE-28223-add-rowid Вот тут проблема, для составных ПК он 
не умеет пока делать хитрости в начале
+        //  надо будет попровить _key для бинарного объекта а потом уже сюда 
вернуться
+        assertQuery("select id, name, age, rowid from PUBLIC.PERSON where 
rowid = ?")
+            .withParams(rowId0)
+            .columnNames("ID", "NAME", "AGE", "ROWID")
+            .matches(QueryChecker.containsIndexScan("PUBLIC", "PERSON", 
"_key_PK"))
+            .returns(0, "foo0", 18, rowId0)
+            .check();
+
+        assertQuery("select p.id, p.name, p.age, p.rowid from PUBLIC.PERSON 
where p.rowid = ?")
+            .withParams(rowId1)
+            .columnNames("ID", "NAME", "AGE", "ROWID")
+            .matches(QueryChecker.containsIndexScan("PUBLIC", "PERSON", 
"_key_PK"))
+            .returns(1, "foo1", 19, rowId1)
             .check();
     }
 
     /** */
-    public static @Nullable Integer toKeyFromRowId(ExecutionContext<?> ctx, 
@Nullable String rowId) {
-        IgniteEx n = (IgniteEx) Ignition.ignite(ctx.localNodeId());
+    public static @Nullable Object toKeyFromRowId(ExecutionContext<?> ctx, 
@Nullable String rowIdBase64) {
+        if (rowIdBase64 == null)
+            return null;
+
+        RowId rowId = RowId.fromBase64String(rowIdBase64);
 
-        return rowId == null ? null : Integer.parseInt(rowId);
+        // TODO: IGNITE-28223-add-rowid Вот тут сейчас линейный поиск, надо бы 
переделать, идеи:
+        //  1. В ExecutionContext положить IgniteEx - думаю самое простое и 
лучшее
+        //  2. В org.apache.ignite.internal.IgnitionEx.grid(java.util.UUID) 
сделать map UUID -> IgniteEx - думаю хуже
+        IgniteEx n = (IgniteEx) IgnitionEx.grid(ctx.localNodeId());
+
+        CacheObjectContext coctx = 
n.context().cache().cacheGroup(rowId.cacheGrpId).cacheObjectContext();
+
+        byte[] bs = coctx.restoreIfNecessary(rowId.valBytes);
+
+        try {
+            return coctx.unmarshal(bs, null);
+        } catch (IgniteCheckedException e) {
+            throw new IgniteException(
+                String.format("Failed to unmarshal %s: [rowIdBase64=%s]", 
RowId.class.getSimpleName(), rowIdBase64), e
+            );
+        }
     }
 
     /** */
@@ -128,9 +197,9 @@ public class RowIdPseudoColumnTest extends 
AbstractBasicIntegrationTest {
                 RowIdPseudoColumnOperatorTable.TO_KEY_FROM_ROW_ID,
                 RexImpTable.createRexCallImplementor((translator, call, 
translatedOperands) -> {
                     var str = Expressions.convert_(translatedOperands.get(0), 
String.class);
-                    var execCtx = Expressions.convert_(translator.getRoot(), 
ExecutionContext.class);
+                    var ectx = Expressions.convert_(translator.getRoot(), 
ExecutionContext.class);
 
-                    return Expressions.call(RowIdPseudoColumnTest.class, 
"toKeyFromRowId", execCtx, str);
+                    return Expressions.call(RowIdPseudoColumnTest.class, 
"toKeyFromRowId", ectx, str);
                 }, NullPolicy.ANY, false)
             );
 
@@ -151,24 +220,55 @@ public class RowIdPseudoColumnTest extends 
AbstractBasicIntegrationTest {
         SqlNode left = operands.get(0);
         SqlNode right = operands.get(1);
 
-        if (!isRowId(left))
+        SqlIdentifier rowIdId = rowIdIdentifier(left);
+        SqlNode rowIdVal = right;
+
+        if (rowIdId == null) {
+            rowIdId = rowIdIdentifier(right);
+            rowIdVal = left;
+        }
+
+        if (rowIdId == null)
             return call;
 
         SqlParserPos pos = call.getParserPosition();
-        SqlIdentifier keyId = new SqlIdentifier(QueryUtils.KEY_FIELD_NAME, 
pos);
-        SqlCall toKey = 
RowIdPseudoColumnOperatorTable.TO_KEY_FROM_ROW_ID.createCall(pos, right);
+        SqlIdentifier keyId = toKeyIdentifier(rowIdId, pos);
+        SqlCall toKey = 
RowIdPseudoColumnOperatorTable.TO_KEY_FROM_ROW_ID.createCall(pos, rowIdVal);
 
         return new SqlBasicCall(SqlStdOperatorTable.EQUALS, List.of(keyId, 
toKey), pos);
     }
 
     /** */
-    private static boolean isRowId(SqlNode node) {
-        if (!(node instanceof SqlIdentifier))
-            return false;
+    private static @Nullable SqlIdentifier rowIdIdentifier(SqlNode node) {
+        // Validator may wrap identifier with CAST/other unary calls during 
type coercion.
+        // Unwrap a simple CAST chain first to still recognize ROWID.
+        SqlNode cur = node;
+
+        while (cur instanceof SqlCall) {
+            SqlCall c = (SqlCall)cur;
 
-        SqlIdentifier identifier = (SqlIdentifier)node;
+            if (c.getKind() != SqlKind.CAST || c.operandCount() < 1)
+                break;
+
+            cur = c.operand(0);
+        }
+
+        if (!(cur instanceof SqlIdentifier))
+            return null;
+
+        SqlIdentifier identifier = (SqlIdentifier)cur;
+
+        String lastName = identifier.names.get(identifier.names.size() - 1);
+
+        return RowIdPseudoColumn.COLUMN_NAME.equalsIgnoreCase(lastName) ? 
identifier : null;
+    }
+
+    /** */
+    private static SqlIdentifier toKeyIdentifier(SqlIdentifier rowIdId, 
SqlParserPos pos) {
+        if (rowIdId.names.size() == 1)
+            return new SqlIdentifier(QueryUtils.KEY_FIELD_NAME, pos);
 
-        return identifier.names.size() == 1 && 
"ROWID".equalsIgnoreCase(identifier.getSimple());
+        return rowIdId.skipLast(1).plus(QueryUtils.KEY_FIELD_NAME, pos);
     }
 
     /** */
@@ -177,18 +277,26 @@ public class RowIdPseudoColumnTest extends 
AbstractBasicIntegrationTest {
         public static final SqlFunction TO_KEY_FROM_ROW_ID = new SqlFunction(
             "TO_KEY_FROM_ROW_ID",
             SqlKind.OTHER_FUNCTION,
-            ReturnTypes.INTEGER,
+            ReturnTypes.explicit(SqlTypeName.ANY),
             null,
-            OperandTypes.STRING,
+            OperandTypes.ANY,
             SqlFunctionCategory.USER_DEFINED_FUNCTION
-        );
+        ) {
+            /** {@inheritDoc} */
+            @Override public boolean isDeterministic() {
+                return false; // важно: чтобы Calcite не константно сворачивал 
в Java-объект
+            }
+        };
     }
 
     /** */
     private static class RowIdPseudoColumn implements PseudoColumnDescriptor {
+        /** */
+        private static final String COLUMN_NAME = "ROWID";
+
         /** {@inheritDoc} */
         @Override public String name() {
-            return "ROWID";
+            return COLUMN_NAME;
         }
 
         /** {@inheritDoc} */
@@ -208,7 +316,88 @@ public class RowIdPseudoColumnTest extends 
AbstractBasicIntegrationTest {
 
         /** {@inheritDoc} */
         @Override public Object value(PseudoColumnValueExtractorContext ctx) 
throws IgniteCheckedException {
-            return ctx.source(true, true).toString();
+            RowId rowId = RowId.of((PseudoColumnValueExtractorContextEx) ctx);
+
+            return RowId.toBase64String(rowId);
+        }
+    }
+
+    /** */
+    private static class RowId {
+        /** */
+        @GridToStringInclude
+        private final int cacheGrpId;
+
+        /** */
+        @GridToStringInclude
+        private final byte[] valBytes;
+
+        /** */
+        private RowId(int cacheGrpId, byte[] valBytes) {
+            this.cacheGrpId = cacheGrpId;
+            this.valBytes = valBytes;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(RowId.class, this);
+        }
+
+        /** */
+        private static RowId of(PseudoColumnValueExtractorContextEx ctx) {
+            byte[] valBytes;
+
+            try {
+                valBytes = 
ctx.source().key().valueBytes(ctx.cacheCtx().cacheObjectContext());
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(
+                    String.format(
+                        "Failed to get valBytes for %s: [cacheName=%s, 
key=%s]",
+                        RowId.class.getSimpleName(), ctx.cacheCtx().name(), 
ctx.source(true, false)),
+                    e
+                );
+            }
+
+            return new RowId(ctx.cacheCtx().groupId(), valBytes);
+        }
+        
+        /** */
+        private static String toBase64String(RowId rowId) {
+            byte[] bs;
+
+            try (
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                DataOutputStream dos = new DataOutputStream(baos)
+            ) {
+                dos.writeInt(rowId.cacheGrpId);
+                U.writeByteArray(dos, rowId.valBytes);
+
+                bs = baos.toByteArray();
+            } catch (IOException e) {
+                throw new IgniteException(
+                    String.format("Failed to serialize %s: [value=%s]", 
RowId.class.getSimpleName(), rowId), e
+                );
+            }
+
+            return Base64.getEncoder().encodeToString(bs);
+        }
+        
+        /** */
+        private static RowId fromBase64String(String s) {
+            byte[] decoded = Base64.getDecoder().decode(s);
+
+            try (
+                ByteArrayInputStream bais = new ByteArrayInputStream(decoded);
+                DataInputStream dis = new DataInputStream(bais)
+            ) {
+                return new RowId(dis.readInt(), U.readByteArray(dis));
+            }
+            catch (IOException e) {
+                throw new IgniteException(
+                    String.format("Failed to deserialize %s: [value=%s]", 
RowId.class.getSimpleName(), s), e
+                );
+            }
         }
     }
 }

Reply via email to